From dd0d64d480188e51d6398a13cb3ef6a14f745e50 Mon Sep 17 00:00:00 2001 From: kumavis Date: Sat, 17 Feb 2024 12:37:09 -1000 Subject: [PATCH 01/27] feat(daemon): store endo formula upon initialization --- packages/daemon/src/daemon-node-powers.js | 7 ++ packages/daemon/src/daemon.js | 102 +++++++++++++++++++--- packages/daemon/src/types.d.ts | 9 ++ 3 files changed, 104 insertions(+), 14 deletions(-) diff --git a/packages/daemon/src/daemon-node-powers.js b/packages/daemon/src/daemon-node-powers.js index ee1b4acdf4..66e53c6f9a 100644 --- a/packages/daemon/src/daemon-node-powers.js +++ b/packages/daemon/src/daemon-node-powers.js @@ -504,6 +504,12 @@ export const makeDaemonicPersistencePowers = ( await Promise.all([statePathP, cachePathP, ephemeralStatePathP]); }; + const isRootInitialized = async () => { + const noncePath = filePowers.joinPath(locator.statePath, 'nonce'); + const nonce = await filePowers.maybeReadFileText(noncePath); + return nonce !== undefined; + }; + const provideRootNonce = async () => { const noncePath = filePowers.joinPath(locator.statePath, 'nonce'); let nonce = await filePowers.maybeReadFileText(noncePath); @@ -636,6 +642,7 @@ export const makeDaemonicPersistencePowers = ( return harden({ initializePersistence, provideRootNonce, + isRootInitialized, makeContentSha512Store, readFormula, writeFormula, diff --git a/packages/daemon/src/daemon.js b/packages/daemon/src/daemon.js index ae81dffa2c..38ab931f16 100644 --- a/packages/daemon/src/daemon.js +++ b/packages/daemon/src/daemon.js @@ -54,6 +54,8 @@ const makeInspector = (type, number, record) => /** * @param {import('./types.js').DaemonicPowers} powers + * @param {import('./types.js').EndoFormula} endoFormula + * @param {string} endoFormulaNumber * @param {Promise} webletPortP * @param {object} args * @param {Promise} args.cancelled @@ -63,6 +65,8 @@ const makeInspector = (type, number, record) => */ const makeEndoBootstrap = async ( powers, + endoFormula, + endoFormulaNumber, webletPortP, { cancelled, cancel, gracePeriodMs, gracePeriodElapsed }, ) => { @@ -79,18 +83,17 @@ const makeEndoBootstrap = async ( return digester.digestHex(); }; - const contentStore = persistencePowers.makeContentSha512Store(); - const rootNonce = await persistencePowers.provideRootNonce(); + const { + host: defaultHostFormulaIdentifier, + webPageJs: webPageJsFormulaIdentifier, + leastAuthority: leastAuthorityFormulaIdentifier, + } = endoFormula; + const endoFormulaIdentifier = `endo:${endoFormulaNumber}`; + const { number: defaultHostFormulaNumber } = parseFormulaIdentifier( + defaultHostFormulaIdentifier, + ); - const endoFormulaIdentifier = `endo:${rootNonce}`; - const defaultHostFormulaNumber = derive(rootNonce, 'host'); - const defaultHostFormulaIdentifier = `host:${defaultHostFormulaNumber}`; - const webPageJsFormulaIdentifier = `web-page-js:${derive( - rootNonce, - 'web-page-js', - )}`; - const leastAuthorityFormulaNumber = derive(rootNonce, 'least-authority'); - const leastAuthorityFormulaIdentifier = `least-authority:${leastAuthorityFormulaNumber}`; + const contentStore = persistencePowers.makeContentSha512Store(); /** @type {Map>} */ const controllerForFormulaIdentifier = new Map(); @@ -462,7 +465,7 @@ const makeEndoBootstrap = async ( context, ); } else if (formulaType === 'endo') { - if (formulaNumber !== rootNonce) { + if (formulaNumber !== endoFormulaNumber) { throw new Error('Invalid endo formula number.'); } @@ -760,7 +763,6 @@ const makeEndoBootstrap = async ( }), ); } - // @ts-expect-error this should never occur return makeInspector(formula.type, formulaNumber, harden({})); }; @@ -808,6 +810,78 @@ const makeEndoBootstrap = async ( return endoBootstrap; }; +/** + * @param {import('./types.js').DaemonicPowers} powers + * @param {Promise} webletPortP + * @param {object} args + * @param {Promise} args.cancelled + * @param {(error: Error) => void} args.cancel + * @param {number} args.gracePeriodMs + * @param {Promise} args.gracePeriodElapsed + */ +const provideEndoBootstrap = async ( + powers, + webletPortP, + { cancelled, cancel, gracePeriodMs, gracePeriodElapsed }, +) => { + const { crypto: cryptoPowers, persistence: persistencePowers } = powers; + const { makeSha512 } = cryptoPowers; + const derive = (...path) => { + const digester = makeSha512(); + digester.updateText(path.join(':')); + return digester.digestHex(); + }; + + const isInitialized = await persistencePowers.isRootInitialized(); + // Reading root nonce before isRootInitialized will cause isRootInitialized to be true. + const rootNonce = await persistencePowers.provideRootNonce(); + if (isInitialized) { + const endoFormula = /** @type {import('./types.js').EndoFormula} */ ( + await persistencePowers.readFormula('endo', rootNonce) + ); + console.log(`Using existing endo formula ${endoFormula}`); + return makeEndoBootstrap(powers, endoFormula, rootNonce, webletPortP, { + cancelled, + cancel, + gracePeriodMs, + gracePeriodElapsed, + }); + } else { + console.log(`Initializing endo formula for root nonce ${rootNonce}`); + + const defaultHostFormulaNumber = derive(rootNonce, 'host'); + const defaultHostFormulaIdentifier = `host:${defaultHostFormulaNumber}`; + const webPageJsFormulaIdentifier = `web-page-js:${derive( + rootNonce, + 'web-page-js', + )}`; + const leastAuthorityFormulaNumber = derive(rootNonce, 'least-authority'); + const leastAuthorityFormulaIdentifier = `least-authority:${leastAuthorityFormulaNumber}`; + + /** @type {import('./types.js').EndoFormula} */ + const endoFormula = { + type: 'endo', + host: defaultHostFormulaIdentifier, + leastAuthority: leastAuthorityFormulaIdentifier, + webPageJs: webPageJsFormulaIdentifier, + }; + await persistencePowers.writeFormula( + endoFormula, + endoFormula.type, + rootNonce, + ); + console.log(`wrote`); + + + return makeEndoBootstrap(powers, endoFormula, rootNonce, webletPortP, { + cancelled, + cancel, + gracePeriodMs, + gracePeriodElapsed, + }); + } +}; + /** * @param {import('./types.js').DaemonicPowers} powers * @param {string} daemonLabel @@ -837,7 +911,7 @@ export const makeDaemon = async (powers, daemonLabel, cancel, cancelled) => { makePromiseKit() ); - const endoBootstrap = makeEndoBootstrap(powers, assignedWebletPortP, { + const endoBootstrap = provideEndoBootstrap(powers, assignedWebletPortP, { cancelled, cancel, gracePeriodMs, diff --git a/packages/daemon/src/types.d.ts b/packages/daemon/src/types.d.ts index 6a81e90da7..b49b82da39 100644 --- a/packages/daemon/src/types.d.ts +++ b/packages/daemon/src/types.d.ts @@ -57,6 +57,13 @@ type FormulaIdentifierRecord = { number: string; }; +type EndoFormula = { + type: 'endo'; + host: string; + leastAuthority: string; + webPageJs: string; +}; + type GuestFormula = { type: 'guest'; host: string; @@ -109,6 +116,7 @@ type WebBundleFormula = { }; export type Formula = + | EndoFormula | GuestFormula | EvalFormula | LookupFormula @@ -457,6 +465,7 @@ export type NetworkPowers = SocketPowers & { export type DaemonicPersistencePowers = { initializePersistence: () => Promise; + isRootInitialized: () => Promise; provideRootNonce: () => Promise; makeContentSha512Store: () => { store: (readable: AsyncIterable) => Promise; From e90e4fa58bb68481038d38e4d84f48fa3c387801 Mon Sep 17 00:00:00 2001 From: kumavis Date: Sat, 17 Feb 2024 16:18:33 -1000 Subject: [PATCH 02/27] refactor(daemon): breakout makeDaemonCore from makeEndoBootstrap --- packages/daemon/src/daemon.js | 149 +++++++++++++++++++++++++--------- 1 file changed, 110 insertions(+), 39 deletions(-) diff --git a/packages/daemon/src/daemon.js b/packages/daemon/src/daemon.js index 38ab931f16..5ba2aefad9 100644 --- a/packages/daemon/src/daemon.js +++ b/packages/daemon/src/daemon.js @@ -63,7 +63,7 @@ const makeInspector = (type, number, record) => * @param {number} args.gracePeriodMs * @param {Promise} args.gracePeriodElapsed */ -const makeEndoBootstrap = async ( +const makeDaemonCore = async ( powers, endoFormula, endoFormulaNumber, @@ -85,7 +85,6 @@ const makeEndoBootstrap = async ( const { host: defaultHostFormulaIdentifier, - webPageJs: webPageJsFormulaIdentifier, leastAuthority: leastAuthorityFormulaIdentifier, } = endoFormula; const endoFormulaIdentifier = `endo:${endoFormulaNumber}`; @@ -465,15 +464,9 @@ const makeEndoBootstrap = async ( context, ); } else if (formulaType === 'endo') { - if (formulaNumber !== endoFormulaNumber) { - throw new Error('Invalid endo formula number.'); - } - - // TODO reframe "cancelled" as termination of the "endo" object and - // ensure that all values ultimately depend on "endo". - // Behold, self-referentiality: - // eslint-disable-next-line no-use-before-define - return { external: endoBootstrap, internal: undefined }; + throw new TypeError( + `Not possible to create endo bootstrap from formulaIdentifier`, + ); } else if (formulaType === 'least-authority') { return { external: leastAuthority, internal: undefined }; } else if (formulaType === 'web-page-js') { @@ -777,22 +770,45 @@ const makeEndoBootstrap = async ( return info; }; + // The only way the endo formula can be loaded is through controllerForFormulaIdentifier. + const setEndoBootstrap = endoBootstrap => { + /** @type {import('./types.js').Controller} */ + const controller = { + external: endoBootstrap, + // @ts-expect-error types say internal cannot be undefined + internal: undefined, + }; + controllerForFormulaIdentifier.set(endoFormulaIdentifier, controller); + }; + + const daemonCore = { + provideValueForFormulaIdentifier, + setEndoBootstrap, + }; + return daemonCore; +}; + +const makeEndoBootstrapFromDaemonCore = async ( + daemonCore, + endoFormula, + { cancelled, cancel, gracePeriodMs, gracePeriodElapsed }, +) => { + const { provideValueForFormulaIdentifier, setEndoBootstrap } = daemonCore; + const { + host: defaultHostFormulaIdentifier, + webPageJs: webPageJsFormulaIdentifier, + } = endoFormula; + const endoBootstrap = Far('Endo private facet', { // TODO for user named - ping: async () => 'pong', - terminate: async () => { cancel(new Error('Termination requested')); }, - host: () => provideValueForFormulaIdentifier(defaultHostFormulaIdentifier), - leastAuthority: () => leastAuthority, - webPageJs: () => provideValueForFormulaIdentifier(webPageJsFormulaIdentifier), - importAndEndowInWebPage: async (webPageP, webPageNumber) => { const { bundle: bundleBlob, powers: endowedPowers } = /** @type {import('./types.js').EndoWebBundle} */ ( @@ -806,7 +822,45 @@ const makeEndoBootstrap = async ( await E(webPageP).makeBundle(bundle, endowedPowers); }, }); + setEndoBootstrap(endoBootstrap); + return endoBootstrap; +}; +/** + * @param {import('./types.js').DaemonicPowers} powers + * @param {import('./types.js').EndoFormula} endoFormula + * @param {string} endoFormulaNumber + * @param {Promise} webletPortP + * @param {object} args + * @param {Promise} args.cancelled + * @param {(error: Error) => void} args.cancel + * @param {number} args.gracePeriodMs + * @param {Promise} args.gracePeriodElapsed + */ +const makeEndoBootstrap = async ( + powers, + endoFormula, + endoFormulaNumber, + webletPortP, + { cancelled, cancel, gracePeriodMs, gracePeriodElapsed }, +) => { + const daemonCore = await makeDaemonCore( + powers, + endoFormula, + endoFormulaNumber, + webletPortP, + { + cancelled, + cancel, + gracePeriodMs, + gracePeriodElapsed, + }, + ); + const endoBootstrap = await makeEndoBootstrapFromDaemonCore( + daemonCore, + endoFormula, + { cancelled, cancel, gracePeriodMs, gracePeriodElapsed }, + ); return endoBootstrap; }; @@ -834,28 +888,34 @@ const provideEndoBootstrap = async ( const isInitialized = await persistencePowers.isRootInitialized(); // Reading root nonce before isRootInitialized will cause isRootInitialized to be true. - const rootNonce = await persistencePowers.provideRootNonce(); + const endoFormulaNumber = await persistencePowers.provideRootNonce(); if (isInitialized) { const endoFormula = /** @type {import('./types.js').EndoFormula} */ ( - await persistencePowers.readFormula('endo', rootNonce) + await persistencePowers.readFormula('endo', endoFormulaNumber) + ); + return makeEndoBootstrap( + powers, + endoFormula, + endoFormulaNumber, + webletPortP, + { + cancelled, + cancel, + gracePeriodMs, + gracePeriodElapsed, + }, ); - console.log(`Using existing endo formula ${endoFormula}`); - return makeEndoBootstrap(powers, endoFormula, rootNonce, webletPortP, { - cancelled, - cancel, - gracePeriodMs, - gracePeriodElapsed, - }); } else { - console.log(`Initializing endo formula for root nonce ${rootNonce}`); - - const defaultHostFormulaNumber = derive(rootNonce, 'host'); + const defaultHostFormulaNumber = derive(endoFormulaNumber, 'host'); const defaultHostFormulaIdentifier = `host:${defaultHostFormulaNumber}`; const webPageJsFormulaIdentifier = `web-page-js:${derive( - rootNonce, + endoFormulaNumber, 'web-page-js', )}`; - const leastAuthorityFormulaNumber = derive(rootNonce, 'least-authority'); + const leastAuthorityFormulaNumber = derive( + endoFormulaNumber, + 'least-authority', + ); const leastAuthorityFormulaIdentifier = `least-authority:${leastAuthorityFormulaNumber}`; /** @type {import('./types.js').EndoFormula} */ @@ -868,17 +928,28 @@ const provideEndoBootstrap = async ( await persistencePowers.writeFormula( endoFormula, endoFormula.type, - rootNonce, + endoFormulaNumber, ); - console.log(`wrote`); + const daemonCore = await makeDaemonCore( + powers, + endoFormula, + endoFormulaNumber, + webletPortP, + { + cancelled, + cancel, + gracePeriodMs, + gracePeriodElapsed, + }, + ); + const endoBootstrap = await makeEndoBootstrapFromDaemonCore( + daemonCore, + endoFormula, + { cancelled, cancel, gracePeriodMs, gracePeriodElapsed }, + ); - return makeEndoBootstrap(powers, endoFormula, rootNonce, webletPortP, { - cancelled, - cancel, - gracePeriodMs, - gracePeriodElapsed, - }); + return endoBootstrap; } }; From 89d9418a39f7cf965ab9ab43e6be6188d0129839 Mon Sep 17 00:00:00 2001 From: kumavis Date: Sat, 17 Feb 2024 18:48:37 -1000 Subject: [PATCH 03/27] feat(daemon): must use incarnateHost to make a new host --- packages/daemon/src/daemon.js | 141 ++++++++++++++++++++------------- packages/daemon/src/host.js | 11 ++- packages/daemon/src/types.d.ts | 10 +++ 3 files changed, 103 insertions(+), 59 deletions(-) diff --git a/packages/daemon/src/daemon.js b/packages/daemon/src/daemon.js index 5ba2aefad9..636fc365af 100644 --- a/packages/daemon/src/daemon.js +++ b/packages/daemon/src/daemon.js @@ -55,7 +55,6 @@ const makeInspector = (type, number, record) => /** * @param {import('./types.js').DaemonicPowers} powers * @param {import('./types.js').EndoFormula} endoFormula - * @param {string} endoFormulaNumber * @param {Promise} webletPortP * @param {object} args * @param {Promise} args.cancelled @@ -66,7 +65,6 @@ const makeInspector = (type, number, record) => const makeDaemonCore = async ( powers, endoFormula, - endoFormulaNumber, webletPortP, { cancelled, cancel, gracePeriodMs, gracePeriodElapsed }, ) => { @@ -83,11 +81,7 @@ const makeDaemonCore = async ( return digester.digestHex(); }; - const { - host: defaultHostFormulaIdentifier, - leastAuthority: leastAuthorityFormulaIdentifier, - } = endoFormula; - const endoFormulaIdentifier = `endo:${endoFormulaNumber}`; + const { host: defaultHostFormulaIdentifier } = endoFormula; const { number: defaultHostFormulaNumber } = parseFormulaIdentifier( defaultHostFormulaIdentifier, ); @@ -374,6 +368,18 @@ const makeDaemonCore = async ( formula.bundle, context, ); + } else if (formula.type === 'host') { + // Behold, recursion: + // eslint-disable-next-line no-use-before-define + return makeIdentifiedHost( + formulaIdentifier, + formula.endo, + formula.petStore, + formula.inspector, + formula.worker, + formula.leastAuthority, + context, + ); } else if (formula.type === 'guest') { const storeFormulaNumber = derive(formulaNumber, 'pet-store'); const storeFormulaIdentifier = `pet-store:${storeFormulaNumber}`; @@ -442,27 +448,6 @@ const makeDaemonCore = async ( assertPetName, ); return { external, internal: undefined }; - } else if (formulaType === 'host') { - const workerFormulaNumber = derive(formulaNumber, 'worker'); - const workerFormulaIdentifier = `worker:${workerFormulaNumber}`; - const inspectorFormulaNumber = derive(formulaNumber, 'pet-inspector'); - const inspectorFormulaIdentifier = `pet-inspector:${inspectorFormulaNumber}`; - // Note the pet store formula number derivation path: - // root -> host -> inspector -> pet store - const storeFormulaNumber = derive(inspectorFormulaNumber, 'pet-store'); - const storeFormulaIdentifier = `pet-store:${storeFormulaNumber}`; - - // Behold, recursion: - // eslint-disable-next-line no-use-before-define - return makeIdentifiedHost( - formulaIdentifier, - endoFormulaIdentifier, - storeFormulaIdentifier, - inspectorFormulaIdentifier, - workerFormulaIdentifier, - leastAuthorityFormulaIdentifier, - context, - ); } else if (formulaType === 'endo') { throw new TypeError( `Not possible to create endo bootstrap from formulaIdentifier`, @@ -492,6 +477,7 @@ const makeDaemonCore = async ( 'eval', 'make-unconfined', 'make-bundle', + 'host', 'guest', 'web-bundle', ].includes(formulaType) @@ -639,11 +625,46 @@ const makeDaemonCore = async ( makeMailbox, }); + /** + * @param {string} endoFormulaIdentifier + * @param {string} leastAuthorityFormulaIdentifier + * @param {string} [specifiedFormulaNumber] + * @returns {Promise<{ formulaIdentifier: string, value: import('./types').EndoHost }>} + */ + const incarnateHost = async ( + endoFormulaIdentifier, + leastAuthorityFormulaIdentifier, + specifiedFormulaNumber, + ) => { + const formulaNumber = specifiedFormulaNumber || (await randomHex512()); + const workerFormulaNumber = derive(formulaNumber, 'worker'); + const workerFormulaIdentifier = `worker:${workerFormulaNumber}`; + const inspectorFormulaNumber = derive(formulaNumber, 'pet-inspector'); + const inspectorFormulaIdentifier = `pet-inspector:${inspectorFormulaNumber}`; + // Note the pet store formula number derivation path: + // root -> host -> inspector -> pet store + const storeFormulaNumber = derive(inspectorFormulaNumber, 'pet-store'); + const storeFormulaIdentifier = `pet-store:${storeFormulaNumber}`; + /** @type {import('./types.js').HostFormula} */ + const formula = { + type: 'host', + petStore: storeFormulaIdentifier, + inspector: inspectorFormulaIdentifier, + worker: workerFormulaIdentifier, + endo: endoFormulaIdentifier, + leastAuthority: leastAuthorityFormulaIdentifier, + }; + return /** @type {Promise<{ formulaIdentifier: string, value: import('./types').EndoHost }>} */ ( + provideValueForNumberedFormula('host', formulaNumber, formula) + ); + }; + const makeIdentifiedHost = makeHostMaker({ provideValueForFormulaIdentifier, provideValueForFormula, provideValueForNumberedFormula, provideControllerForFormulaIdentifier, + incarnateHost, storeReaderRef, randomHex512, makeSha512, @@ -771,7 +792,14 @@ const makeDaemonCore = async ( }; // The only way the endo formula can be loaded is through controllerForFormulaIdentifier. - const setEndoBootstrap = endoBootstrap => { + const setEndoBootstrap = (endoFormulaIdentifier, endoBootstrap) => { + const { type } = parseFormulaIdentifier(endoFormulaIdentifier); + if (type !== 'endo') { + throw new TypeError( + `Invalid formula identifier, expected type 'endo', got ${q(type)}`, + ); + } + /** @type {import('./types.js').Controller} */ const controller = { external: endoBootstrap, @@ -783,6 +811,7 @@ const makeDaemonCore = async ( const daemonCore = { provideValueForFormulaIdentifier, + incarnateHost, setEndoBootstrap, }; return daemonCore; @@ -791,6 +820,7 @@ const makeDaemonCore = async ( const makeEndoBootstrapFromDaemonCore = async ( daemonCore, endoFormula, + endoFormulaIdentifier, { cancelled, cancel, gracePeriodMs, gracePeriodElapsed }, ) => { const { provideValueForFormulaIdentifier, setEndoBootstrap } = daemonCore; @@ -822,14 +852,14 @@ const makeEndoBootstrapFromDaemonCore = async ( await E(webPageP).makeBundle(bundle, endowedPowers); }, }); - setEndoBootstrap(endoBootstrap); + setEndoBootstrap(endoFormulaIdentifier, endoBootstrap); return endoBootstrap; }; /** * @param {import('./types.js').DaemonicPowers} powers * @param {import('./types.js').EndoFormula} endoFormula - * @param {string} endoFormulaNumber + * @param {string} endoFormulaIdentifier * @param {Promise} webletPortP * @param {object} args * @param {Promise} args.cancelled @@ -840,25 +870,20 @@ const makeEndoBootstrapFromDaemonCore = async ( const makeEndoBootstrap = async ( powers, endoFormula, - endoFormulaNumber, + endoFormulaIdentifier, webletPortP, { cancelled, cancel, gracePeriodMs, gracePeriodElapsed }, ) => { - const daemonCore = await makeDaemonCore( - powers, - endoFormula, - endoFormulaNumber, - webletPortP, - { - cancelled, - cancel, - gracePeriodMs, - gracePeriodElapsed, - }, - ); + const daemonCore = await makeDaemonCore(powers, endoFormula, webletPortP, { + cancelled, + cancel, + gracePeriodMs, + gracePeriodElapsed, + }); const endoBootstrap = await makeEndoBootstrapFromDaemonCore( daemonCore, endoFormula, + endoFormulaIdentifier, { cancelled, cancel, gracePeriodMs, gracePeriodElapsed }, ); return endoBootstrap; @@ -889,6 +914,8 @@ const provideEndoBootstrap = async ( const isInitialized = await persistencePowers.isRootInitialized(); // Reading root nonce before isRootInitialized will cause isRootInitialized to be true. const endoFormulaNumber = await persistencePowers.provideRootNonce(); + const endoFormulaIdentifier = `endo:${endoFormulaNumber}`; + if (isInitialized) { const endoFormula = /** @type {import('./types.js').EndoFormula} */ ( await persistencePowers.readFormula('endo', endoFormulaNumber) @@ -896,7 +923,7 @@ const provideEndoBootstrap = async ( return makeEndoBootstrap( powers, endoFormula, - endoFormulaNumber, + endoFormulaIdentifier, webletPortP, { cancelled, @@ -931,21 +958,23 @@ const provideEndoBootstrap = async ( endoFormulaNumber, ); - const daemonCore = await makeDaemonCore( - powers, - endoFormula, - endoFormulaNumber, - webletPortP, - { - cancelled, - cancel, - gracePeriodMs, - gracePeriodElapsed, - }, + const daemonCore = await makeDaemonCore(powers, endoFormula, webletPortP, { + cancelled, + cancel, + gracePeriodMs, + gracePeriodElapsed, + }); + // Ensure the default host is incarnated and persisted. + await daemonCore.incarnateHost( + endoFormulaIdentifier, + leastAuthorityFormulaIdentifier, + defaultHostFormulaNumber, ); + const endoBootstrap = await makeEndoBootstrapFromDaemonCore( daemonCore, endoFormula, + endoFormulaIdentifier, { cancelled, cancel, gracePeriodMs, gracePeriodElapsed }, ); diff --git a/packages/daemon/src/host.js b/packages/daemon/src/host.js index fdf86f225b..524dca84b1 100644 --- a/packages/daemon/src/host.js +++ b/packages/daemon/src/host.js @@ -10,6 +10,7 @@ export const makeHostMaker = ({ provideValueForFormula, provideValueForNumberedFormula, provideControllerForFormulaIdentifier, + incarnateHost, storeReaderRef, makeSha512, randomHex512, @@ -410,12 +411,16 @@ export const makeHostMaker = ({ formulaIdentifier = identifyLocal(petName); } if (formulaIdentifier === undefined) { - const id512 = await randomHex512(); - formulaIdentifier = `host:${id512}`; + const { formulaIdentifier: newFormulaIdentifier, value } = + await incarnateHost( + endoFormulaIdentifier, + leastAuthorityFormulaIdentifier, + ); if (petName !== undefined) { assertPetName(petName); - await petStore.write(petName, formulaIdentifier); + await petStore.write(petName, newFormulaIdentifier); } + return { formulaIdentifier: newFormulaIdentifier, value }; } else if (!formulaIdentifier.startsWith('host:')) { throw new Error( `Existing pet name does not designate a host powers capability: ${q( diff --git a/packages/daemon/src/types.d.ts b/packages/daemon/src/types.d.ts index b49b82da39..5f702832be 100644 --- a/packages/daemon/src/types.d.ts +++ b/packages/daemon/src/types.d.ts @@ -64,6 +64,15 @@ type EndoFormula = { webPageJs: string; }; +type HostFormula = { + type: 'host'; + worker: string; + inspector: string; + petStore: string; + endo: string; + leastAuthority: string; +}; + type GuestFormula = { type: 'guest'; host: string; @@ -117,6 +126,7 @@ type WebBundleFormula = { export type Formula = | EndoFormula + | HostFormula | GuestFormula | EvalFormula | LookupFormula From 1f694c73600af7ea8cbcc8fe9d9d3901e4cce2f3 Mon Sep 17 00:00:00 2001 From: kumavis Date: Sat, 17 Feb 2024 22:11:26 -1000 Subject: [PATCH 04/27] feat(daemon): must use incarnateBundler to make the web-page-js bundler --- packages/daemon/src/daemon.js | 74 ++++++++++++++++++++-------------- packages/daemon/src/types.d.ts | 2 +- 2 files changed, 44 insertions(+), 32 deletions(-) diff --git a/packages/daemon/src/daemon.js b/packages/daemon/src/daemon.js index 636fc365af..bbfb91bdc0 100644 --- a/packages/daemon/src/daemon.js +++ b/packages/daemon/src/daemon.js @@ -54,7 +54,6 @@ const makeInspector = (type, number, record) => /** * @param {import('./types.js').DaemonicPowers} powers - * @param {import('./types.js').EndoFormula} endoFormula * @param {Promise} webletPortP * @param {object} args * @param {Promise} args.cancelled @@ -64,7 +63,6 @@ const makeInspector = (type, number, record) => */ const makeDaemonCore = async ( powers, - endoFormula, webletPortP, { cancelled, cancel, gracePeriodMs, gracePeriodElapsed }, ) => { @@ -81,11 +79,6 @@ const makeDaemonCore = async ( return digester.digestHex(); }; - const { host: defaultHostFormulaIdentifier } = endoFormula; - const { number: defaultHostFormulaNumber } = parseFormulaIdentifier( - defaultHostFormulaIdentifier, - ); - const contentStore = persistencePowers.makeContentSha512Store(); /** @type {Map>} */ @@ -454,24 +447,6 @@ const makeDaemonCore = async ( ); } else if (formulaType === 'least-authority') { return { external: leastAuthority, internal: undefined }; - } else if (formulaType === 'web-page-js') { - if (persistencePowers.getWebPageBundlerFormula === undefined) { - throw Error('No web-page-js formula provided.'); - } - // Note that this worker is hardcoded to be the "MAIN" worker of the - // default host. - const workerFormulaNumber = derive(defaultHostFormulaNumber, 'worker'); - const workerFormulaIdentifier = `worker:${workerFormulaNumber}`; - - return makeControllerForFormula( - 'web-page-js', - derive(formulaNumber, 'web-page-js'), - persistencePowers.getWebPageBundlerFormula( - workerFormulaIdentifier, - defaultHostFormulaIdentifier, - ), - context, - ); } else if ( [ 'eval', @@ -480,6 +455,7 @@ const makeDaemonCore = async ( 'host', 'guest', 'web-bundle', + 'web-page-js', ].includes(formulaType) ) { const formula = await persistencePowers.readFormula( @@ -659,6 +635,30 @@ const makeDaemonCore = async ( ); }; + /** + * @param {string} powersFormulaIdentifier + * @param {string} workerFormulaIdentifier + * @param {string} [specifiedFormulaNumber] + * @returns {Promise<{ formulaIdentifier: string, value: unknown }>} + */ + const incarnateBundler = async ( + powersFormulaIdentifier, + workerFormulaIdentifier, + specifiedFormulaNumber, + ) => { + if (persistencePowers.getWebPageBundlerFormula === undefined) { + throw Error('No web-page-js bundler formula provided.'); + } + const formulaNumber = specifiedFormulaNumber || (await randomHex512()); + const formula = persistencePowers.getWebPageBundlerFormula( + powersFormulaIdentifier, + workerFormulaIdentifier, + ); + return /** @type {Promise<{ formulaIdentifier: string, value: unknown }>} */ ( + provideValueForNumberedFormula(formula.type, formulaNumber, formula) + ); + }; + const makeIdentifiedHost = makeHostMaker({ provideValueForFormulaIdentifier, provideValueForFormula, @@ -812,6 +812,7 @@ const makeDaemonCore = async ( const daemonCore = { provideValueForFormulaIdentifier, incarnateHost, + incarnateBundler, setEndoBootstrap, }; return daemonCore; @@ -874,7 +875,7 @@ const makeEndoBootstrap = async ( webletPortP, { cancelled, cancel, gracePeriodMs, gracePeriodElapsed }, ) => { - const daemonCore = await makeDaemonCore(powers, endoFormula, webletPortP, { + const daemonCore = await makeDaemonCore(powers, webletPortP, { cancelled, cancel, gracePeriodMs, @@ -935,10 +936,13 @@ const provideEndoBootstrap = async ( } else { const defaultHostFormulaNumber = derive(endoFormulaNumber, 'host'); const defaultHostFormulaIdentifier = `host:${defaultHostFormulaNumber}`; - const webPageJsFormulaIdentifier = `web-page-js:${derive( - endoFormulaNumber, - 'web-page-js', - )}`; + const defaultHostWorkerFormulaNumber = derive( + defaultHostFormulaNumber, + 'worker', + ); + const defaultHostWorkerFormulaIdentifier = `worker:${defaultHostWorkerFormulaNumber}`; + const webPageJsFormulaNumber = derive(endoFormulaNumber, 'web-page-js'); + const webPageJsFormulaIdentifier = `make-unconfined:${webPageJsFormulaNumber}`; const leastAuthorityFormulaNumber = derive( endoFormulaNumber, 'least-authority', @@ -958,7 +962,7 @@ const provideEndoBootstrap = async ( endoFormulaNumber, ); - const daemonCore = await makeDaemonCore(powers, endoFormula, webletPortP, { + const daemonCore = await makeDaemonCore(powers, webletPortP, { cancelled, cancel, gracePeriodMs, @@ -970,6 +974,14 @@ const provideEndoBootstrap = async ( leastAuthorityFormulaIdentifier, defaultHostFormulaNumber, ); + // If supported, ensure the web page bundler is incarnated and persisted. + if (persistencePowers.getWebPageBundlerFormula !== undefined) { + await daemonCore.incarnateBundler( + defaultHostFormulaIdentifier, + defaultHostWorkerFormulaIdentifier, + webPageJsFormulaNumber, + ); + } const endoBootstrap = await makeEndoBootstrapFromDaemonCore( daemonCore, diff --git a/packages/daemon/src/types.d.ts b/packages/daemon/src/types.d.ts index 5f702832be..0571ceecea 100644 --- a/packages/daemon/src/types.d.ts +++ b/packages/daemon/src/types.d.ts @@ -490,7 +490,7 @@ export type DaemonicPersistencePowers = { getWebPageBundlerFormula?: ( workerFormulaIdentifier: string, powersFormulaIdentifier: string, - ) => Formula; + ) => MakeUnconfinedFormula; }; export interface DaemonWorkerFacet {} From d598be728527f37f59741de9862bde5b48acf1fc Mon Sep 17 00:00:00 2001 From: kumavis Date: Sat, 17 Feb 2024 22:06:13 -1000 Subject: [PATCH 05/27] refactor(daemon): incarnate endo daemon dependencies before endo formula --- packages/daemon/src/daemon.js | 86 +++++++++++++--------------------- packages/daemon/src/types.d.ts | 2 +- 2 files changed, 34 insertions(+), 54 deletions(-) diff --git a/packages/daemon/src/daemon.js b/packages/daemon/src/daemon.js index bbfb91bdc0..fe93462aa2 100644 --- a/packages/daemon/src/daemon.js +++ b/packages/daemon/src/daemon.js @@ -604,17 +604,17 @@ const makeDaemonCore = async ( /** * @param {string} endoFormulaIdentifier * @param {string} leastAuthorityFormulaIdentifier - * @param {string} [specifiedFormulaNumber] + * @param {string} [specifiedWorkerFormulaIdentifier] * @returns {Promise<{ formulaIdentifier: string, value: import('./types').EndoHost }>} */ const incarnateHost = async ( endoFormulaIdentifier, leastAuthorityFormulaIdentifier, - specifiedFormulaNumber, + specifiedWorkerFormulaIdentifier, ) => { - const formulaNumber = specifiedFormulaNumber || (await randomHex512()); - const workerFormulaNumber = derive(formulaNumber, 'worker'); - const workerFormulaIdentifier = `worker:${workerFormulaNumber}`; + const formulaNumber = await randomHex512(); + const workerFormulaIdentifier = + specifiedWorkerFormulaIdentifier || `worker:${await randomHex512()}`; const inspectorFormulaNumber = derive(formulaNumber, 'pet-inspector'); const inspectorFormulaIdentifier = `pet-inspector:${inspectorFormulaNumber}`; // Note the pet store formula number derivation path: @@ -638,25 +638,21 @@ const makeDaemonCore = async ( /** * @param {string} powersFormulaIdentifier * @param {string} workerFormulaIdentifier - * @param {string} [specifiedFormulaNumber] * @returns {Promise<{ formulaIdentifier: string, value: unknown }>} */ const incarnateBundler = async ( powersFormulaIdentifier, workerFormulaIdentifier, - specifiedFormulaNumber, ) => { if (persistencePowers.getWebPageBundlerFormula === undefined) { throw Error('No web-page-js bundler formula provided.'); } - const formulaNumber = specifiedFormulaNumber || (await randomHex512()); + const formulaNumber = await randomHex512(); const formula = persistencePowers.getWebPageBundlerFormula( powersFormulaIdentifier, workerFormulaIdentifier, ); - return /** @type {Promise<{ formulaIdentifier: string, value: unknown }>} */ ( - provideValueForNumberedFormula(formula.type, formulaNumber, formula) - ); + return provideValueForNumberedFormula(formula.type, formulaNumber, formula); }; const makeIdentifiedHost = makeHostMaker({ @@ -905,13 +901,7 @@ const provideEndoBootstrap = async ( { cancelled, cancel, gracePeriodMs, gracePeriodElapsed }, ) => { const { crypto: cryptoPowers, persistence: persistencePowers } = powers; - const { makeSha512 } = cryptoPowers; - const derive = (...path) => { - const digester = makeSha512(); - digester.updateText(path.join(':')); - return digester.digestHex(); - }; - + const { randomHex512 } = cryptoPowers; const isInitialized = await persistencePowers.isRootInitialized(); // Reading root nonce before isRootInitialized will cause isRootInitialized to be true. const endoFormulaNumber = await persistencePowers.provideRootNonce(); @@ -934,20 +924,31 @@ const provideEndoBootstrap = async ( }, ); } else { - const defaultHostFormulaNumber = derive(endoFormulaNumber, 'host'); - const defaultHostFormulaIdentifier = `host:${defaultHostFormulaNumber}`; - const defaultHostWorkerFormulaNumber = derive( - defaultHostFormulaNumber, - 'worker', - ); - const defaultHostWorkerFormulaIdentifier = `worker:${defaultHostWorkerFormulaNumber}`; - const webPageJsFormulaNumber = derive(endoFormulaNumber, 'web-page-js'); - const webPageJsFormulaIdentifier = `make-unconfined:${webPageJsFormulaNumber}`; - const leastAuthorityFormulaNumber = derive( - endoFormulaNumber, - 'least-authority', - ); - const leastAuthorityFormulaIdentifier = `least-authority:${leastAuthorityFormulaNumber}`; + const leastAuthorityFormulaIdentifier = `least-authority:${await randomHex512()}`; + const defaultHostWorkerFormulaIdentifier = `worker:${await randomHex512()}`; + + const daemonCore = await makeDaemonCore(powers, webletPortP, { + cancelled, + cancel, + gracePeriodMs, + gracePeriodElapsed, + }); + // Ensure the default host is incarnated and persisted. + const { formulaIdentifier: defaultHostFormulaIdentifier } = + await daemonCore.incarnateHost( + endoFormulaIdentifier, + leastAuthorityFormulaIdentifier, + defaultHostWorkerFormulaIdentifier, + ); + // If supported, ensure the web page bundler is incarnated and persisted. + let webPageJsFormulaIdentifier; + if (persistencePowers.getWebPageBundlerFormula !== undefined) { + ({ formulaIdentifier: webPageJsFormulaIdentifier } = + await daemonCore.incarnateBundler( + defaultHostFormulaIdentifier, + defaultHostWorkerFormulaIdentifier, + )); + } /** @type {import('./types.js').EndoFormula} */ const endoFormula = { @@ -962,27 +963,6 @@ const provideEndoBootstrap = async ( endoFormulaNumber, ); - const daemonCore = await makeDaemonCore(powers, webletPortP, { - cancelled, - cancel, - gracePeriodMs, - gracePeriodElapsed, - }); - // Ensure the default host is incarnated and persisted. - await daemonCore.incarnateHost( - endoFormulaIdentifier, - leastAuthorityFormulaIdentifier, - defaultHostFormulaNumber, - ); - // If supported, ensure the web page bundler is incarnated and persisted. - if (persistencePowers.getWebPageBundlerFormula !== undefined) { - await daemonCore.incarnateBundler( - defaultHostFormulaIdentifier, - defaultHostWorkerFormulaIdentifier, - webPageJsFormulaNumber, - ); - } - const endoBootstrap = await makeEndoBootstrapFromDaemonCore( daemonCore, endoFormula, diff --git a/packages/daemon/src/types.d.ts b/packages/daemon/src/types.d.ts index 0571ceecea..316d5f2388 100644 --- a/packages/daemon/src/types.d.ts +++ b/packages/daemon/src/types.d.ts @@ -61,7 +61,7 @@ type EndoFormula = { type: 'endo'; host: string; leastAuthority: string; - webPageJs: string; + webPageJs?: string; }; type HostFormula = { From f92d7511e6899773594cb4cd22d1d726db3f0cb4 Mon Sep 17 00:00:00 2001 From: kumavis Date: Sun, 18 Feb 2024 10:58:40 -1000 Subject: [PATCH 06/27] feat(daemon): add handle and provideControllerForFormulaIdentifierAndResolveHandle --- packages/daemon/src/daemon.js | 54 +++++++++++++++++++++++++++++++- packages/daemon/src/guest.js | 24 ++++++-------- packages/daemon/src/host.js | 14 +++++++-- packages/daemon/src/mail.js | 24 +++++++------- packages/daemon/src/pet-store.js | 2 +- packages/daemon/src/types.d.ts | 17 +++++++++- 6 files changed, 105 insertions(+), 30 deletions(-) diff --git a/packages/daemon/src/daemon.js b/packages/daemon/src/daemon.js index fe93462aa2..4f016de3a1 100644 --- a/packages/daemon/src/daemon.js +++ b/packages/daemon/src/daemon.js @@ -405,6 +405,14 @@ const makeDaemonCore = async ( }))(), internal: undefined, }; + } else if (formula.type === 'handle') { + context.thisDiesIfThatDies(formula.target); + return { + external: {}, + internal: { + targetFormulaIdentifier: formula.target, + }, + }; } else { throw new TypeError(`Invalid formula: ${q(formula)}`); } @@ -456,6 +464,7 @@ const makeDaemonCore = async ( 'guest', 'web-bundle', 'web-page-js', + 'handle', ].includes(formulaType) ) { const formula = await persistencePowers.readFormula( @@ -582,6 +591,31 @@ const makeDaemonCore = async ( return value; }; + /** @type {import('./types.js').ProvideControllerForFormulaIdentifierAndResolveHandle} */ + const provideControllerForFormulaIdentifierAndResolveHandle = + async formulaIdentifier => { + let currentFormulaIdentifier = formulaIdentifier; + // eslint-disable-next-line no-constant-condition + while (true) { + const controller = provideControllerForFormulaIdentifier( + currentFormulaIdentifier, + ); + // eslint-disable-next-line no-await-in-loop + const internalFacet = await controller.internal; + if (internalFacet === undefined || internalFacet === null) { + return controller; + } + // @ts-expect-error We can't know the type of the internal facet. + if (internalFacet.targetFormulaIdentifier === undefined) { + return controller; + } + const handle = /** @type {import('./types.js').InternalHandle} */ ( + internalFacet + ); + currentFormulaIdentifier = handle.targetFormulaIdentifier; + } + }; + const makeContext = makeContextMaker({ controllerForFormulaIdentifier, provideControllerForFormulaIdentifier, @@ -593,14 +627,31 @@ const makeDaemonCore = async ( provideControllerForFormulaIdentifier, makeSha512, provideValueForNumberedFormula, + provideControllerForFormulaIdentifierAndResolveHandle, }); const makeIdentifiedGuestController = makeGuestMaker({ provideValueForFormulaIdentifier, - provideControllerForFormulaIdentifier, + provideControllerForFormulaIdentifierAndResolveHandle, makeMailbox, }); + /** + * @param {string} targetFormulaIdentifier + * @returns {Promise<{ formulaIdentifier: string, value: import('./types').Handle }>} + */ + const incarnateHandle = async targetFormulaIdentifier => { + const formulaNumber = await randomHex512(); + /** @type {import('./types.js').HandleFormula} */ + const formula = { + type: 'handle', + target: targetFormulaIdentifier, + }; + return /** @type {Promise<{ formulaIdentifier: string, value: import('./types').Handle }>} */ ( + provideValueForNumberedFormula(formula.type, formulaNumber, formula) + ); + }; + /** * @param {string} endoFormulaIdentifier * @param {string} leastAuthorityFormulaIdentifier @@ -661,6 +712,7 @@ const makeDaemonCore = async ( provideValueForNumberedFormula, provideControllerForFormulaIdentifier, incarnateHost, + incarnateHandle, storeReaderRef, randomHex512, makeSha512, diff --git a/packages/daemon/src/guest.js b/packages/daemon/src/guest.js index 9a3b203146..bdbe432589 100644 --- a/packages/daemon/src/guest.js +++ b/packages/daemon/src/guest.js @@ -4,40 +4,36 @@ import { Far } from '@endo/far'; export const makeGuestMaker = ({ provideValueForFormulaIdentifier, - provideControllerForFormulaIdentifier, + provideControllerForFormulaIdentifierAndResolveHandle, makeMailbox, }) => { /** * @param {string} guestFormulaIdentifier - * @param {string} hostFormulaIdentifier + * @param {string} hostHandleFormulaIdentifier * @param {string} petStoreFormulaIdentifier * @param {string} mainWorkerFormulaIdentifier * @param {import('./types.js').Context} context */ const makeIdentifiedGuestController = async ( guestFormulaIdentifier, - hostFormulaIdentifier, + hostHandleFormulaIdentifier, petStoreFormulaIdentifier, mainWorkerFormulaIdentifier, context, ) => { - context.thisDiesIfThatDies(hostFormulaIdentifier); + context.thisDiesIfThatDies(hostHandleFormulaIdentifier); context.thisDiesIfThatDies(petStoreFormulaIdentifier); context.thisDiesIfThatDies(mainWorkerFormulaIdentifier); const petStore = /** @type {import('./types.js').PetStore} */ ( await provideValueForFormulaIdentifier(petStoreFormulaIdentifier) ); - const hostController = /** @type {import('./types.js').Controller<>} */ ( - await provideControllerForFormulaIdentifier(hostFormulaIdentifier) - ); - const hostPrivateFacet = await hostController.internal; - if (hostPrivateFacet === undefined) { - throw new Error( - `panic: a host request function must exist for every host`, + const hostController = + await provideControllerForFormulaIdentifierAndResolveHandle( + hostHandleFormulaIdentifier, ); - } - const { respond: deliverToHost } = hostPrivateFacet; + const hostPrivateFacet = await hostController.internal; + const { respond: deliverToHost } = /** @type {any} */ (hostPrivateFacet); if (deliverToHost === undefined) { throw new Error( `panic: a host request function must exist for every host`, @@ -67,7 +63,7 @@ export const makeGuestMaker = ({ selfFormulaIdentifier: guestFormulaIdentifier, specialNames: { SELF: guestFormulaIdentifier, - HOST: hostFormulaIdentifier, + HOST: hostHandleFormulaIdentifier, }, context, }); diff --git a/packages/daemon/src/host.js b/packages/daemon/src/host.js index 524dca84b1..512efe462a 100644 --- a/packages/daemon/src/host.js +++ b/packages/daemon/src/host.js @@ -11,6 +11,7 @@ export const makeHostMaker = ({ provideValueForNumberedFormula, provideControllerForFormulaIdentifier, incarnateHost, + incarnateHandle, storeReaderRef, makeSha512, randomHex512, @@ -76,6 +77,13 @@ export const makeHostMaker = ({ context, }); + /** + * @returns {Promise<{ formulaIdentifier: string, value: import('./types').Handle }>} + */ + const makeNewHandleForSelf = () => { + return incarnateHandle(hostFormulaIdentifier); + }; + /** * @param {import('./types.js').Controller} newController * @param {Record} introducedNames @@ -110,10 +118,12 @@ export const makeHostMaker = ({ } if (formulaIdentifier === undefined) { + const { formulaIdentifier: hostHandleFormulaIdentifier } = + await makeNewHandleForSelf(); /** @type {import('./types.js').GuestFormula} */ const formula = { - type: /* @type {'guest'} */ 'guest', - host: hostFormulaIdentifier, + type: 'guest', + host: hostHandleFormulaIdentifier, }; const { value, formulaIdentifier: guestFormulaIdentifier } = // Behold, recursion: diff --git a/packages/daemon/src/mail.js b/packages/daemon/src/mail.js index fe060114fa..67027ef71c 100644 --- a/packages/daemon/src/mail.js +++ b/packages/daemon/src/mail.js @@ -15,6 +15,7 @@ const { quote: q } = assert; * @param {import('./types.js').GetFormulaIdentifierForRef} args.getFormulaIdentifierForRef * @param {import('./types.js').MakeSha512} args.makeSha512 * @param {import('./types.js').ProvideValueForNumberedFormula} args.provideValueForNumberedFormula + * @param {import('./types.js').ProvideControllerForFormulaIdentifierAndResolveHandle} args.provideControllerForFormulaIdentifierAndResolveHandle */ export const makeMailboxMaker = ({ provideValueForFormulaIdentifier, @@ -22,6 +23,7 @@ export const makeMailboxMaker = ({ getFormulaIdentifierForRef, makeSha512, provideValueForNumberedFormula, + provideControllerForFormulaIdentifierAndResolveHandle, }) => { /** * @param {object} args @@ -375,9 +377,10 @@ export const makeMailboxMaker = ({ if (recipientFormulaIdentifier === undefined) { throw new Error(`Unknown pet name for party: ${recipientName}`); } - const recipientController = await provideControllerForFormulaIdentifier( - recipientFormulaIdentifier, - ); + const recipientController = + await provideControllerForFormulaIdentifierAndResolveHandle( + recipientFormulaIdentifier, + ); const recipientInternal = await recipientController.internal; if (recipientInternal === undefined || recipientInternal === null) { throw new Error(`Recipient cannot receive messages: ${recipientName}`); @@ -416,7 +419,6 @@ export const makeMailboxMaker = ({ strings, edgeNames, formulaIdentifiers, - recipientFormulaIdentifier, ); // add to own mailbox receive( @@ -424,6 +426,7 @@ export const makeMailboxMaker = ({ strings, edgeNames, formulaIdentifiers, + // Sender expects the handle formula identifier. recipientFormulaIdentifier, ); }; @@ -486,20 +489,18 @@ export const makeMailboxMaker = ({ throw new Error(`Unknown pet name for party: ${recipientName}`); } const recipientController = - /** @type {import('./types.js').Controller<>} */ ( - await provideControllerForFormulaIdentifier( - recipientFormulaIdentifier, - ) + await provideControllerForFormulaIdentifierAndResolveHandle( + recipientFormulaIdentifier, ); - const recipientInternal = await recipientController.internal; - if (recipientInternal === undefined) { + if (recipientInternal === undefined || recipientInternal === null) { throw new Error( `panic: a receive request function must exist for every party`, ); } - const { respond: deliverToRecipient } = await recipientInternal; + // @ts-expect-error We sufficiently check if recipientInternal or deliverToRecipient is undefined + const { respond: deliverToRecipient } = recipientInternal; if (deliverToRecipient === undefined) { throw new Error( `panic: a receive request function must exist for every party`, @@ -528,6 +529,7 @@ export const makeMailboxMaker = ({ responseName, selfFormulaIdentifier, petStore, + // Sender expects the handle formula identifier. recipientFormulaIdentifier, ); const newResponseP = Promise.race([recipientResponseP, selfResponseP]); diff --git a/packages/daemon/src/pet-store.js b/packages/daemon/src/pet-store.js index 4e052adb14..6dc4c8d8db 100644 --- a/packages/daemon/src/pet-store.js +++ b/packages/daemon/src/pet-store.js @@ -9,7 +9,7 @@ const { quote: q } = assert; const validIdPattern = /^[0-9a-f]{128}$/; const validFormulaPattern = - /^(?:(?:readable-blob|worker|pet-store|pet-inspector|eval|lookup|make-unconfined|make-bundle|host|guest):[0-9a-f]{128}|web-bundle:[0-9a-f]{32})$/; + /^(?:(?:readable-blob|worker|pet-store|pet-inspector|eval|lookup|make-unconfined|make-bundle|host|guest|handle):[0-9a-f]{128}|web-bundle:[0-9a-f]{32})$/; /** * @param {import('./types.js').FilePowers} filePowers diff --git a/packages/daemon/src/types.d.ts b/packages/daemon/src/types.d.ts index 316d5f2388..4f25c75ec5 100644 --- a/packages/daemon/src/types.d.ts +++ b/packages/daemon/src/types.d.ts @@ -124,6 +124,11 @@ type WebBundleFormula = { powers: string; }; +type HandleFormula = { + type: 'handle'; + target: string; +}; + export type Formula = | EndoFormula | HostFormula @@ -132,7 +137,8 @@ export type Formula = | LookupFormula | MakeUnconfinedFormula | MakeBundleFormula - | WebBundleFormula; + | WebBundleFormula + | HandleFormula; export type Label = { number: number; @@ -206,6 +212,15 @@ export type ProvideValueForFormulaIdentifier = ( export type ProvideControllerForFormulaIdentifier = ( formulaIdentifier: string, ) => Controller; +export type ProvideControllerForFormulaIdentifierAndResolveHandle = ( + formulaIdentifier: string, +) => Promise; + +export interface Handle {} +export interface InternalHandle { + targetFormulaIdentifier: string; +} + export type GetFormulaIdentifierForRef = (ref: unknown) => string | undefined; export type MakeSha512 = () => Sha512; From c5a001cc86952430cf00155ec46245b8d54ad9ff Mon Sep 17 00:00:00 2001 From: kumavis Date: Sun, 18 Feb 2024 13:17:57 -1000 Subject: [PATCH 07/27] refactor(daemon): first time init via incarnateEndoBootstrap --- packages/daemon/src/daemon.js | 260 +++++++++++++-------------------- packages/daemon/src/types.d.ts | 13 +- 2 files changed, 114 insertions(+), 159 deletions(-) diff --git a/packages/daemon/src/daemon.js b/packages/daemon/src/daemon.js index 4f016de3a1..3c562d8862 100644 --- a/packages/daemon/src/daemon.js +++ b/packages/daemon/src/daemon.js @@ -413,6 +413,49 @@ const makeDaemonCore = async ( targetFormulaIdentifier: formula.target, }, }; + } else if (formula.type === 'endo') { + /** @type {import('./types.js').EndoBootstrap} */ + const endoBootstrap = Far('Endo private facet', { + // TODO for user named + ping: async () => 'pong', + terminate: async () => { + cancel(new Error('Termination requested')); + }, + host: () => { + // Behold, recursion: + return /** @type {Promise} */ ( + // eslint-disable-next-line no-use-before-define + provideValueForFormulaIdentifier(formula.host) + ); + }, + leastAuthority: async () => leastAuthority, + webPageJs: () => { + if (formula.webPageJs === undefined) { + throw new Error('No web-page-js formula provided.'); + } + // Behold, recursion: + // eslint-disable-next-line no-use-before-define + return provideValueForFormulaIdentifier(formula.webPageJs); + }, + importAndEndowInWebPage: async (webPageP, webPageNumber) => { + const { bundle: bundleBlob, powers: endowedPowers } = + /** @type {import('./types.js').EndoWebBundle} */ ( + // Behold, recursion: + // eslint-disable-next-line no-use-before-define + await provideValueForFormulaIdentifier( + `web-bundle:${webPageNumber}`, + ).catch(() => { + throw new Error('Not found'); + }) + ); + const bundle = await E(bundleBlob).json(); + await E(webPageP).makeBundle(bundle, endowedPowers); + }, + }); + return { + external: endoBootstrap, + internal: undefined, + }; } else { throw new TypeError(`Invalid formula: ${q(formula)}`); } @@ -449,14 +492,11 @@ const makeDaemonCore = async ( assertPetName, ); return { external, internal: undefined }; - } else if (formulaType === 'endo') { - throw new TypeError( - `Not possible to create endo bootstrap from formulaIdentifier`, - ); } else if (formulaType === 'least-authority') { return { external: leastAuthority, internal: undefined }; } else if ( [ + 'endo', 'eval', 'make-unconfined', 'make-bundle', @@ -706,6 +746,46 @@ const makeDaemonCore = async ( return provideValueForNumberedFormula(formula.type, formulaNumber, formula); }; + /** + * @param {string} [specifiedFormulaNumber] + * @returns {Promise<{ formulaIdentifier: string, value: import('./types').EndoBootstrap }>} + */ + const incarnateEndoBootstrap = async specifiedFormulaNumber => { + const formulaNumber = specifiedFormulaNumber || (await randomHex512()); + const endoFormulaIdentifier = `endo:${formulaNumber}`; + + const leastAuthorityFormulaIdentifier = `least-authority:${await randomHex512()}`; + const defaultHostWorkerFormulaIdentifier = `worker:${await randomHex512()}`; + + // Ensure the default host is incarnated and persisted. + const { formulaIdentifier: defaultHostFormulaIdentifier } = + await incarnateHost( + endoFormulaIdentifier, + leastAuthorityFormulaIdentifier, + defaultHostWorkerFormulaIdentifier, + ); + // If supported, ensure the web page bundler is incarnated and persisted. + let webPageJsFormulaIdentifier; + if (persistencePowers.getWebPageBundlerFormula !== undefined) { + ({ formulaIdentifier: webPageJsFormulaIdentifier } = + await incarnateBundler( + defaultHostFormulaIdentifier, + defaultHostWorkerFormulaIdentifier, + )); + } + + /** @type {import('./types.js').EndoFormula} */ + const formula = { + type: 'endo', + host: defaultHostFormulaIdentifier, + leastAuthority: leastAuthorityFormulaIdentifier, + webPageJs: webPageJsFormulaIdentifier, + }; + return /** @type {Promise<{ formulaIdentifier: string, value: import('./types').EndoBootstrap }>} */ ( + provideValueForNumberedFormula(formula.type, formulaNumber, formula) + ); + }; + const makeIdentifiedHost = makeHostMaker({ provideValueForFormulaIdentifier, provideValueForFormula, @@ -839,189 +919,51 @@ const makeDaemonCore = async ( return info; }; - // The only way the endo formula can be loaded is through controllerForFormulaIdentifier. - const setEndoBootstrap = (endoFormulaIdentifier, endoBootstrap) => { - const { type } = parseFormulaIdentifier(endoFormulaIdentifier); - if (type !== 'endo') { - throw new TypeError( - `Invalid formula identifier, expected type 'endo', got ${q(type)}`, - ); - } - - /** @type {import('./types.js').Controller} */ - const controller = { - external: endoBootstrap, - // @ts-expect-error types say internal cannot be undefined - internal: undefined, - }; - controllerForFormulaIdentifier.set(endoFormulaIdentifier, controller); - }; - const daemonCore = { provideValueForFormulaIdentifier, + incarnateEndoBootstrap, incarnateHost, incarnateBundler, - setEndoBootstrap, }; return daemonCore; }; -const makeEndoBootstrapFromDaemonCore = async ( - daemonCore, - endoFormula, - endoFormulaIdentifier, - { cancelled, cancel, gracePeriodMs, gracePeriodElapsed }, -) => { - const { provideValueForFormulaIdentifier, setEndoBootstrap } = daemonCore; - const { - host: defaultHostFormulaIdentifier, - webPageJs: webPageJsFormulaIdentifier, - } = endoFormula; - - const endoBootstrap = Far('Endo private facet', { - // TODO for user named - ping: async () => 'pong', - terminate: async () => { - cancel(new Error('Termination requested')); - }, - host: () => provideValueForFormulaIdentifier(defaultHostFormulaIdentifier), - leastAuthority: () => leastAuthority, - webPageJs: () => - provideValueForFormulaIdentifier(webPageJsFormulaIdentifier), - importAndEndowInWebPage: async (webPageP, webPageNumber) => { - const { bundle: bundleBlob, powers: endowedPowers } = - /** @type {import('./types.js').EndoWebBundle} */ ( - await provideValueForFormulaIdentifier( - `web-bundle:${webPageNumber}`, - ).catch(() => { - throw new Error('Not found'); - }) - ); - const bundle = await E(bundleBlob).json(); - await E(webPageP).makeBundle(bundle, endowedPowers); - }, - }); - setEndoBootstrap(endoFormulaIdentifier, endoBootstrap); - return endoBootstrap; -}; - /** * @param {import('./types.js').DaemonicPowers} powers - * @param {import('./types.js').EndoFormula} endoFormula - * @param {string} endoFormulaIdentifier * @param {Promise} webletPortP * @param {object} args * @param {Promise} args.cancelled * @param {(error: Error) => void} args.cancel * @param {number} args.gracePeriodMs * @param {Promise} args.gracePeriodElapsed + * @returns {Promise} */ -const makeEndoBootstrap = async ( +const provideEndoBootstrap = async ( powers, - endoFormula, - endoFormulaIdentifier, webletPortP, { cancelled, cancel, gracePeriodMs, gracePeriodElapsed }, ) => { + const { persistence: persistencePowers } = powers; + const daemonCore = await makeDaemonCore(powers, webletPortP, { cancelled, cancel, gracePeriodMs, gracePeriodElapsed, }); - const endoBootstrap = await makeEndoBootstrapFromDaemonCore( - daemonCore, - endoFormula, - endoFormulaIdentifier, - { cancelled, cancel, gracePeriodMs, gracePeriodElapsed }, - ); - return endoBootstrap; -}; -/** - * @param {import('./types.js').DaemonicPowers} powers - * @param {Promise} webletPortP - * @param {object} args - * @param {Promise} args.cancelled - * @param {(error: Error) => void} args.cancel - * @param {number} args.gracePeriodMs - * @param {Promise} args.gracePeriodElapsed - */ -const provideEndoBootstrap = async ( - powers, - webletPortP, - { cancelled, cancel, gracePeriodMs, gracePeriodElapsed }, -) => { - const { crypto: cryptoPowers, persistence: persistencePowers } = powers; - const { randomHex512 } = cryptoPowers; const isInitialized = await persistencePowers.isRootInitialized(); // Reading root nonce before isRootInitialized will cause isRootInitialized to be true. const endoFormulaNumber = await persistencePowers.provideRootNonce(); - const endoFormulaIdentifier = `endo:${endoFormulaNumber}`; - if (isInitialized) { - const endoFormula = /** @type {import('./types.js').EndoFormula} */ ( - await persistencePowers.readFormula('endo', endoFormulaNumber) - ); - return makeEndoBootstrap( - powers, - endoFormula, - endoFormulaIdentifier, - webletPortP, - { - cancelled, - cancel, - gracePeriodMs, - gracePeriodElapsed, - }, + const endoFormulaIdentifier = `endo:${endoFormulaNumber}`; + return /** @type {Promise} */ ( + daemonCore.provideValueForFormulaIdentifier(endoFormulaIdentifier) ); } else { - const leastAuthorityFormulaIdentifier = `least-authority:${await randomHex512()}`; - const defaultHostWorkerFormulaIdentifier = `worker:${await randomHex512()}`; - - const daemonCore = await makeDaemonCore(powers, webletPortP, { - cancelled, - cancel, - gracePeriodMs, - gracePeriodElapsed, - }); - // Ensure the default host is incarnated and persisted. - const { formulaIdentifier: defaultHostFormulaIdentifier } = - await daemonCore.incarnateHost( - endoFormulaIdentifier, - leastAuthorityFormulaIdentifier, - defaultHostWorkerFormulaIdentifier, - ); - // If supported, ensure the web page bundler is incarnated and persisted. - let webPageJsFormulaIdentifier; - if (persistencePowers.getWebPageBundlerFormula !== undefined) { - ({ formulaIdentifier: webPageJsFormulaIdentifier } = - await daemonCore.incarnateBundler( - defaultHostFormulaIdentifier, - defaultHostWorkerFormulaIdentifier, - )); - } - - /** @type {import('./types.js').EndoFormula} */ - const endoFormula = { - type: 'endo', - host: defaultHostFormulaIdentifier, - leastAuthority: leastAuthorityFormulaIdentifier, - webPageJs: webPageJsFormulaIdentifier, - }; - await persistencePowers.writeFormula( - endoFormula, - endoFormula.type, + const { value: endoBootstrap } = await daemonCore.incarnateEndoBootstrap( endoFormulaNumber, ); - - const endoBootstrap = await makeEndoBootstrapFromDaemonCore( - daemonCore, - endoFormula, - endoFormulaIdentifier, - { cancelled, cancel, gracePeriodMs, gracePeriodElapsed }, - ); - return endoBootstrap; } }; @@ -1055,12 +997,16 @@ export const makeDaemon = async (powers, daemonLabel, cancel, cancelled) => { makePromiseKit() ); - const endoBootstrap = provideEndoBootstrap(powers, assignedWebletPortP, { - cancelled, - cancel, - gracePeriodMs, - gracePeriodElapsed, - }); + const endoBootstrap = await provideEndoBootstrap( + powers, + assignedWebletPortP, + { + cancelled, + cancel, + gracePeriodMs, + gracePeriodElapsed, + }, + ); return { endoBootstrap, cancelGracePeriod, assignWebletPort }; }; diff --git a/packages/daemon/src/types.d.ts b/packages/daemon/src/types.d.ts index 4f25c75ec5..eb722ea665 100644 --- a/packages/daemon/src/types.d.ts +++ b/packages/daemon/src/types.d.ts @@ -418,6 +418,15 @@ export type EndoWebBundle = { powers: ERef; }; +export type EndoBootstrap = FarRef<{ + ping: () => Promise; + terminate: () => Promise; + host: () => Promise; + leastAuthority: () => Promise; + webPageJs: () => Promise; + importAndEndowInWebPage: () => Promise; +}>; + export type CryptoPowers = { makeSha512: () => Sha512; randomHex512: () => Promise; @@ -474,13 +483,13 @@ export type NetworkPowers = SocketPowers & { cancelled: Promise; }) => Promise; makePrivatePathService: ( - endoBootstrap: FarRef, + endoBootstrap: EndoBootstrap, sockPath: string, cancelled: Promise, exitWithError: (error: Error) => void, ) => { started: Promise; stopped: Promise }; makePrivateHttpService: ( - endoBootstrap: FarRef, + endoBootstrap: EndoBootstrap, port: number, assignWebletPort: (portP: Promise) => void, cancelled: Promise, From e82a7b75ec3d2737afd9590eaa03b2b657132303 Mon Sep 17 00:00:00 2001 From: kumavis Date: Sun, 18 Feb 2024 12:36:12 -1000 Subject: [PATCH 08/27] feat(daemon): add incarnateGuest --- packages/daemon/src/daemon.js | 17 +++++++++++++++++ packages/daemon/src/host.js | 8 ++------ 2 files changed, 19 insertions(+), 6 deletions(-) diff --git a/packages/daemon/src/daemon.js b/packages/daemon/src/daemon.js index 3c562d8862..5543aabf94 100644 --- a/packages/daemon/src/daemon.js +++ b/packages/daemon/src/daemon.js @@ -726,6 +726,22 @@ const makeDaemonCore = async ( ); }; + /** + * @param {string} hostHandleFormulaIdentifier + * @returns {Promise<{ formulaIdentifier: string, value: import('./types').EndoGuest }>} + */ + const incarnateGuest = async hostHandleFormulaIdentifier => { + const formulaNumber = await randomHex512(); + /** @type {import('./types.js').GuestFormula} */ + const formula = { + type: 'guest', + host: hostHandleFormulaIdentifier, + }; + return /** @type {Promise<{ formulaIdentifier: string, value: import('./types').EndoGuest }>} */ ( + provideValueForNumberedFormula(formula.type, formulaNumber, formula) + ); + }; + /** * @param {string} powersFormulaIdentifier * @param {string} workerFormulaIdentifier @@ -792,6 +808,7 @@ const makeDaemonCore = async ( provideValueForNumberedFormula, provideControllerForFormulaIdentifier, incarnateHost, + incarnateGuest, incarnateHandle, storeReaderRef, randomHex512, diff --git a/packages/daemon/src/host.js b/packages/daemon/src/host.js index 512efe462a..76d2ccdedd 100644 --- a/packages/daemon/src/host.js +++ b/packages/daemon/src/host.js @@ -11,6 +11,7 @@ export const makeHostMaker = ({ provideValueForNumberedFormula, provideControllerForFormulaIdentifier, incarnateHost, + incarnateGuest, incarnateHandle, storeReaderRef, makeSha512, @@ -120,15 +121,10 @@ export const makeHostMaker = ({ if (formulaIdentifier === undefined) { const { formulaIdentifier: hostHandleFormulaIdentifier } = await makeNewHandleForSelf(); - /** @type {import('./types.js').GuestFormula} */ - const formula = { - type: 'guest', - host: hostHandleFormulaIdentifier, - }; const { value, formulaIdentifier: guestFormulaIdentifier } = // Behold, recursion: // eslint-disable-next-line no-use-before-define - await provideValueForFormula(formula, 'guest'); + await incarnateGuest(hostHandleFormulaIdentifier); if (petName !== undefined) { assertPetName(petName); await petStore.write(petName, guestFormulaIdentifier); From 073d46d1e3db2cecb4cd6f3b55ce6ab3caa39b05 Mon Sep 17 00:00:00 2001 From: kumavis Date: Sun, 18 Feb 2024 13:31:27 -1000 Subject: [PATCH 09/27] feat(daemon): add incarnateLeastAuthority --- packages/daemon/src/daemon.js | 45 +++++++++++++++++++++++++--------- packages/daemon/src/types.d.ts | 5 ++++ 2 files changed, 38 insertions(+), 12 deletions(-) diff --git a/packages/daemon/src/daemon.js b/packages/daemon/src/daemon.js index 5543aabf94..328d294350 100644 --- a/packages/daemon/src/daemon.js +++ b/packages/daemon/src/daemon.js @@ -14,13 +14,6 @@ import { assertPetName } from './pet-name.js'; import { makeContextMaker } from './context.js'; import { parseFormulaIdentifier } from './formula-identifier.js'; -/** @type {import('./types.js').EndoGuest} */ -const leastAuthority = Far('EndoGuest', { - async request() { - throw new Error('declined'); - }, -}); - const delay = async (ms, cancelled) => { // Do not attempt to set up a timer if already cancelled. await Promise.race([cancelled, undefined]); @@ -428,7 +421,13 @@ const makeDaemonCore = async ( provideValueForFormulaIdentifier(formula.host) ); }, - leastAuthority: async () => leastAuthority, + leastAuthority: () => { + // Behold, recursion: + return /** @type {Promise} */ ( + // eslint-disable-next-line no-use-before-define + provideValueForFormulaIdentifier(formula.leastAuthority) + ); + }, webPageJs: () => { if (formula.webPageJs === undefined) { throw new Error('No web-page-js formula provided.'); @@ -456,6 +455,14 @@ const makeDaemonCore = async ( external: endoBootstrap, internal: undefined, }; + } else if (formula.type === 'least-authority') { + /** @type {import('./types.js').EndoGuest} */ + const leastAuthority = Far('EndoGuest', { + async request() { + throw new Error('declined'); + }, + }); + return { external: leastAuthority, internal: undefined }; } else { throw new TypeError(`Invalid formula: ${q(formula)}`); } @@ -492,8 +499,6 @@ const makeDaemonCore = async ( assertPetName, ); return { external, internal: undefined }; - } else if (formulaType === 'least-authority') { - return { external: leastAuthority, internal: undefined }; } else if ( [ 'endo', @@ -502,6 +507,7 @@ const makeDaemonCore = async ( 'make-bundle', 'host', 'guest', + 'least-authority', 'web-bundle', 'web-page-js', 'handle', @@ -676,6 +682,20 @@ const makeDaemonCore = async ( makeMailbox, }); + /** + * @returns {Promise<{ formulaIdentifier: string, value: import('./types').EndoGuest }>} + */ + const incarnateLeastAuthority = async () => { + const formulaNumber = await randomHex512(); + /** @type {import('./types.js').LeastAuthorityFormula} */ + const formula = { + type: 'least-authority', + }; + return /** @type {Promise<{ formulaIdentifier: string, value: import('./types').EndoGuest }>} */ ( + provideValueForNumberedFormula(formula.type, formulaNumber, formula) + ); + }; + /** * @param {string} targetFormulaIdentifier * @returns {Promise<{ formulaIdentifier: string, value: import('./types').Handle }>} @@ -769,10 +789,11 @@ const makeDaemonCore = async ( const incarnateEndoBootstrap = async specifiedFormulaNumber => { const formulaNumber = specifiedFormulaNumber || (await randomHex512()); const endoFormulaIdentifier = `endo:${formulaNumber}`; - - const leastAuthorityFormulaIdentifier = `least-authority:${await randomHex512()}`; const defaultHostWorkerFormulaIdentifier = `worker:${await randomHex512()}`; + const { formulaIdentifier: leastAuthorityFormulaIdentifier } = + await incarnateLeastAuthority(); + // Ensure the default host is incarnated and persisted. const { formulaIdentifier: defaultHostFormulaIdentifier } = await incarnateHost( diff --git a/packages/daemon/src/types.d.ts b/packages/daemon/src/types.d.ts index eb722ea665..822b694e31 100644 --- a/packages/daemon/src/types.d.ts +++ b/packages/daemon/src/types.d.ts @@ -78,6 +78,10 @@ type GuestFormula = { host: string; }; +type LeastAuthorityFormula = { + type: 'least-authority'; +}; + type EvalFormula = { type: 'eval'; worker: string; @@ -133,6 +137,7 @@ export type Formula = | EndoFormula | HostFormula | GuestFormula + | LeastAuthorityFormula | EvalFormula | LookupFormula | MakeUnconfinedFormula From 0bd689d2da01ad306caaa63f897e0a4555dfb5f3 Mon Sep 17 00:00:00 2001 From: kumavis Date: Sun, 18 Feb 2024 14:34:11 -1000 Subject: [PATCH 10/27] feat(daemon): add incarnateEval --- packages/daemon/src/daemon.js | 28 ++++++++++++++++++++++++++++ packages/daemon/src/host.js | 20 +++++++------------- 2 files changed, 35 insertions(+), 13 deletions(-) diff --git a/packages/daemon/src/daemon.js b/packages/daemon/src/daemon.js index 328d294350..7f935794fc 100644 --- a/packages/daemon/src/daemon.js +++ b/packages/daemon/src/daemon.js @@ -762,6 +762,33 @@ const makeDaemonCore = async ( ); }; + /** + * @param {string} workerFormulaIdentifier + * @param {string} source + * @param {string[]} codeNames + * @param {string[]} endowmentFormulaIdentifiers + * @returns {Promise<{ formulaIdentifier: string, value: unknown }>} + */ + const incarnateEval = async ( + workerFormulaIdentifier, + source, + codeNames, + endowmentFormulaIdentifiers, + ) => { + const formulaNumber = await randomHex512(); + /** @type {import('./types.js').EvalFormula} */ + const formula = { + type: 'eval', + worker: workerFormulaIdentifier, + source, + names: codeNames, + values: endowmentFormulaIdentifiers, + }; + return /** @type {Promise<{ formulaIdentifier: string, value: unknown }>} */ ( + provideValueForNumberedFormula(formula.type, formulaNumber, formula) + ); + }; + /** * @param {string} powersFormulaIdentifier * @param {string} workerFormulaIdentifier @@ -830,6 +857,7 @@ const makeDaemonCore = async ( provideControllerForFormulaIdentifier, incarnateHost, incarnateGuest, + incarnateEval, incarnateHandle, storeReaderRef, randomHex512, diff --git a/packages/daemon/src/host.js b/packages/daemon/src/host.js index 76d2ccdedd..08eaaf1e69 100644 --- a/packages/daemon/src/host.js +++ b/packages/daemon/src/host.js @@ -12,6 +12,7 @@ export const makeHostMaker = ({ provideControllerForFormulaIdentifier, incarnateHost, incarnateGuest, + incarnateEval, incarnateHandle, storeReaderRef, makeSha512, @@ -264,7 +265,7 @@ export const makeHostMaker = ({ throw new Error('Evaluator requires one pet name for each code name'); } - const formulaIdentifiers = await Promise.all( + const endowmentFormulaIdentifiers = await Promise.all( petNamePaths.map(async (petNameOrPath, index) => { if (typeof codeNames[index] !== 'string') { throw new Error(`Invalid endowment name: ${q(codeNames[index])}`); @@ -285,20 +286,13 @@ export const makeHostMaker = ({ }), ); - const evalFormula = { - /** @type {'eval'} */ - type: 'eval', - worker: workerFormulaIdentifier, - source, - names: codeNames, - values: formulaIdentifiers, - }; - // Behold, recursion: // eslint-disable-next-line no-use-before-define - const { formulaIdentifier, value } = await provideValueForFormula( - evalFormula, - 'eval', + const { formulaIdentifier, value } = await incarnateEval( + workerFormulaIdentifier, + source, + codeNames, + endowmentFormulaIdentifiers, ); if (resultName !== undefined) { await petStore.write(resultName, formulaIdentifier); From 628f9035de70431921b5e9ce9c8f85f2901dbc27 Mon Sep 17 00:00:00 2001 From: kumavis Date: Sun, 18 Feb 2024 18:09:17 -1000 Subject: [PATCH 11/27] feat(daemon): add incarnateReadableBlob --- packages/daemon/src/daemon.js | 32 +++++++++++++++++++++++++------- packages/daemon/src/types.d.ts | 7 +++++++ 2 files changed, 32 insertions(+), 7 deletions(-) diff --git a/packages/daemon/src/daemon.js b/packages/daemon/src/daemon.js index 7f935794fc..2a0c7fb06e 100644 --- a/packages/daemon/src/daemon.js +++ b/packages/daemon/src/daemon.js @@ -84,6 +84,7 @@ const makeDaemonCore = async ( /** * @param {string} sha512 + * @returns {import('./types.js').FarEndoReadable} */ const makeReadableBlob = sha512 => { const { text, json, streamBase64 } = contentStore.fetch(sha512); @@ -100,7 +101,9 @@ const makeDaemonCore = async ( */ const storeReaderRef = async readerRef => { const sha512Hex = await contentStore.store(makeRefReader(readerRef)); - return `readable-blob:${sha512Hex}`; + // eslint-disable-next-line no-use-before-define + const { formulaIdentifier } = await incarnateReadableBlob(sha512Hex); + return formulaIdentifier; }; /** @@ -338,6 +341,9 @@ const makeDaemonCore = async ( formula.values, context, ); + } else if (formula.type === 'readable-blob') { + const external = makeReadableBlob(formula.content); + return { external, internal: undefined }; } else if (formula.type === 'lookup') { return makeControllerForLookup(formula.hub, formula.path, context); } else if (formula.type === 'make-unconfined') { @@ -479,12 +485,7 @@ const makeDaemonCore = async ( context, ) => { const formulaIdentifier = `${formulaType}:${formulaNumber}`; - if (formulaType === 'readable-blob') { - // Behold, forward-reference: - // eslint-disable-next-line no-use-before-define - const external = makeReadableBlob(formulaNumber); - return { external, internal: undefined }; - } else if (formulaType === 'worker') { + if (formulaType === 'worker') { return makeIdentifiedWorkerController(formulaNumber, context); } else if (formulaType === 'pet-inspector') { const storeFormulaNumber = derive(formulaNumber, 'pet-store'); @@ -503,6 +504,7 @@ const makeDaemonCore = async ( [ 'endo', 'eval', + 'readable-blob', 'make-unconfined', 'make-bundle', 'host', @@ -789,6 +791,22 @@ const makeDaemonCore = async ( ); }; + /** + * @param {string} contentSha512 + * @returns {Promise<{ formulaIdentifier: string, value: import('./types.js').FarEndoReadable }>} + */ + const incarnateReadableBlob = async contentSha512 => { + const formulaNumber = await randomHex512(); + /** @type {import('./types.js').ReadableBlobFormula} */ + const formula = { + type: 'readable-blob', + content: contentSha512, + }; + return /** @type {Promise<{ formulaIdentifier: string, value: import('./types.js').FarEndoReadable }>} */ ( + provideValueForNumberedFormula(formula.type, formulaNumber, formula) + ); + }; + /** * @param {string} powersFormulaIdentifier * @param {string} workerFormulaIdentifier diff --git a/packages/daemon/src/types.d.ts b/packages/daemon/src/types.d.ts index 822b694e31..1f632b7974 100644 --- a/packages/daemon/src/types.d.ts +++ b/packages/daemon/src/types.d.ts @@ -91,6 +91,11 @@ type EvalFormula = { // TODO formula slots }; +type ReadableBlobFormula = { + type: 'readable-blob'; + content: string; +}; + type LookupFormula = { type: 'lookup'; @@ -139,6 +144,7 @@ export type Formula = | GuestFormula | LeastAuthorityFormula | EvalFormula + | ReadableBlobFormula | LookupFormula | MakeUnconfinedFormula | MakeBundleFormula @@ -344,6 +350,7 @@ export interface EndoReadable { text(): Promise; json(): Promise; } +export type FarEndoReadable = FarRef; export interface EndoWorker { terminate(): void; From b391c258d9085cc624880d4fc99d6d1e2831ccc8 Mon Sep 17 00:00:00 2001 From: kumavis Date: Sun, 18 Feb 2024 14:44:44 -1000 Subject: [PATCH 12/27] feat(daemon): add incarnateUnconfined --- packages/daemon/src/daemon.js | 25 +++++++++++++++++++++++++ packages/daemon/src/host.js | 16 +++++----------- 2 files changed, 30 insertions(+), 11 deletions(-) diff --git a/packages/daemon/src/daemon.js b/packages/daemon/src/daemon.js index 2a0c7fb06e..8f1c8a0f92 100644 --- a/packages/daemon/src/daemon.js +++ b/packages/daemon/src/daemon.js @@ -791,6 +791,30 @@ const makeDaemonCore = async ( ); }; + /** + * @param {string} workerFormulaIdentifier + * @param {string} powersFormulaIdentifiers + * @param {string} specifier + * @returns {Promise<{ formulaIdentifier: string, value: unknown }>} + */ + const incarnateUnconfined = async ( + workerFormulaIdentifier, + powersFormulaIdentifiers, + specifier, + ) => { + const formulaNumber = await randomHex512(); + /** @type {import('./types.js').MakeUnconfinedFormula} */ + const formula = { + type: 'make-unconfined', + worker: workerFormulaIdentifier, + powers: powersFormulaIdentifiers, + specifier, + }; + return /** @type {Promise<{ formulaIdentifier: string, value: unknown }>} */ ( + provideValueForNumberedFormula(formula.type, formulaNumber, formula) + ); + }; + /** * @param {string} contentSha512 * @returns {Promise<{ formulaIdentifier: string, value: import('./types.js').FarEndoReadable }>} @@ -876,6 +900,7 @@ const makeDaemonCore = async ( incarnateHost, incarnateGuest, incarnateEval, + incarnateUnconfined, incarnateHandle, storeReaderRef, randomHex512, diff --git a/packages/daemon/src/host.js b/packages/daemon/src/host.js index 08eaaf1e69..fdb7692c47 100644 --- a/packages/daemon/src/host.js +++ b/packages/daemon/src/host.js @@ -13,6 +13,7 @@ export const makeHostMaker = ({ incarnateHost, incarnateGuest, incarnateEval, + incarnateUnconfined, incarnateHandle, storeReaderRef, makeSha512, @@ -315,19 +316,12 @@ export const makeHostMaker = ({ powersName, ); - const formula = { - /** @type {'make-unconfined'} */ - type: 'make-unconfined', - worker: workerFormulaIdentifier, - powers: powersFormulaIdentifier, - specifier, - }; - // Behold, recursion: // eslint-disable-next-line no-use-before-define - const { formulaIdentifier, value } = await provideValueForFormula( - formula, - 'make-unconfined', + const { formulaIdentifier, value } = await incarnateUnconfined( + workerFormulaIdentifier, + powersFormulaIdentifier, + specifier, ); if (resultName !== undefined) { await petStore.write(resultName, formulaIdentifier); From 41fa16b967113347c403c27188436e4386a48760 Mon Sep 17 00:00:00 2001 From: kumavis Date: Sun, 18 Feb 2024 18:27:39 -1000 Subject: [PATCH 13/27] feat(daemon): add incarnateBundle --- packages/daemon/src/daemon.js | 23 +++++++++++++++++++++++ packages/daemon/src/host.js | 16 +++++----------- 2 files changed, 28 insertions(+), 11 deletions(-) diff --git a/packages/daemon/src/daemon.js b/packages/daemon/src/daemon.js index 8f1c8a0f92..a06c313490 100644 --- a/packages/daemon/src/daemon.js +++ b/packages/daemon/src/daemon.js @@ -851,6 +851,28 @@ const makeDaemonCore = async ( return provideValueForNumberedFormula(formula.type, formulaNumber, formula); }; + /** + * @param {string} powersFormulaIdentifier + * @param {string} workerFormulaIdentifier + * @param {string} bundleFormulaIdentifier + * @returns {Promise<{ formulaIdentifier: string, value: unknown }>} + */ + const incarnateBundle = async ( + powersFormulaIdentifier, + workerFormulaIdentifier, + bundleFormulaIdentifier, + ) => { + const formulaNumber = await randomHex512(); + /** @type {import('./types.js').MakeBundleFormula} */ + const formula = { + type: 'make-bundle', + worker: workerFormulaIdentifier, + powers: powersFormulaIdentifier, + bundle: bundleFormulaIdentifier, + }; + return provideValueForNumberedFormula(formula.type, formulaNumber, formula); + }; + /** * @param {string} [specifiedFormulaNumber] * @returns {Promise<{ formulaIdentifier: string, value: import('./types').EndoBootstrap }>} @@ -901,6 +923,7 @@ const makeDaemonCore = async ( incarnateGuest, incarnateEval, incarnateUnconfined, + incarnateBundle, incarnateHandle, storeReaderRef, randomHex512, diff --git a/packages/daemon/src/host.js b/packages/daemon/src/host.js index fdb7692c47..641e43daa6 100644 --- a/packages/daemon/src/host.js +++ b/packages/daemon/src/host.js @@ -14,6 +14,7 @@ export const makeHostMaker = ({ incarnateGuest, incarnateEval, incarnateUnconfined, + incarnateBundle, incarnateHandle, storeReaderRef, makeSha512, @@ -354,19 +355,12 @@ export const makeHostMaker = ({ powersName, ); - const formula = { - /** @type {'make-bundle'} */ - type: 'make-bundle', - worker: workerFormulaIdentifier, - powers: powersFormulaIdentifier, - bundle: bundleFormulaIdentifier, - }; - // Behold, recursion: // eslint-disable-next-line no-use-before-define - const { value, formulaIdentifier } = await provideValueForFormula( - formula, - 'make-bundle', + const { value, formulaIdentifier } = await incarnateBundle( + powersFormulaIdentifier, + workerFormulaIdentifier, + bundleFormulaIdentifier, ); if (resultName !== undefined) { From bbcdc119bbcca91308fe6f28d7fa34e84494b790 Mon Sep 17 00:00:00 2001 From: kumavis Date: Sun, 18 Feb 2024 18:33:00 -1000 Subject: [PATCH 14/27] feat(daemon): add incarnateWebBundle --- packages/daemon/src/daemon.js | 32 ++++++++++++++++++++------------ packages/daemon/src/host.js | 24 ++++-------------------- 2 files changed, 24 insertions(+), 32 deletions(-) diff --git a/packages/daemon/src/daemon.js b/packages/daemon/src/daemon.js index a06c313490..96bdd71e18 100644 --- a/packages/daemon/src/daemon.js +++ b/packages/daemon/src/daemon.js @@ -584,15 +584,6 @@ const makeDaemonCore = async ( }); }; - /** - * @param {import('./types.js').Formula} formula - * @param {string} formulaType - */ - const provideValueForFormula = async (formula, formulaType) => { - const formulaNumber = await randomHex512(); - return provideValueForNumberedFormula(formulaType, formulaNumber, formula); - }; - /** @type {import('./types.js').ProvideControllerForFormulaIdentifier} */ const provideControllerForFormulaIdentifier = formulaIdentifier => { const { type: formulaType, number: formulaNumber } = @@ -873,6 +864,25 @@ const makeDaemonCore = async ( return provideValueForNumberedFormula(formula.type, formulaNumber, formula); }; + /** + * @param {string} powersFormulaIdentifier + * @param {string} bundleFormulaIdentifier + * @returns {Promise<{ formulaIdentifier: string, value: unknown }>} + */ + const incarnateWebBundle = async ( + powersFormulaIdentifier, + bundleFormulaIdentifier, + ) => { + const formulaNumber = await randomHex512(); + /** @type {import('./types.js').WebBundleFormula} */ + const formula = { + type: 'web-bundle', + powers: powersFormulaIdentifier, + bundle: bundleFormulaIdentifier, + }; + return provideValueForNumberedFormula(formula.type, formulaNumber, formula); + }; + /** * @param {string} [specifiedFormulaNumber] * @returns {Promise<{ formulaIdentifier: string, value: import('./types').EndoBootstrap }>} @@ -916,18 +926,16 @@ const makeDaemonCore = async ( const makeIdentifiedHost = makeHostMaker({ provideValueForFormulaIdentifier, - provideValueForFormula, - provideValueForNumberedFormula, provideControllerForFormulaIdentifier, incarnateHost, incarnateGuest, incarnateEval, incarnateUnconfined, incarnateBundle, + incarnateWebBundle, incarnateHandle, storeReaderRef, randomHex512, - makeSha512, makeMailbox, }); diff --git a/packages/daemon/src/host.js b/packages/daemon/src/host.js index 641e43daa6..e72b58107e 100644 --- a/packages/daemon/src/host.js +++ b/packages/daemon/src/host.js @@ -7,17 +7,15 @@ const { quote: q } = assert; export const makeHostMaker = ({ provideValueForFormulaIdentifier, - provideValueForFormula, - provideValueForNumberedFormula, provideControllerForFormulaIdentifier, incarnateHost, incarnateGuest, incarnateEval, incarnateUnconfined, incarnateBundle, + incarnateWebBundle, incarnateHandle, storeReaderRef, - makeSha512, randomHex512, makeMailbox, }) => { @@ -452,24 +450,10 @@ export const makeHostMaker = ({ powersName, ); - const digester = makeSha512(); - digester.updateText( - `${bundleFormulaIdentifier},${powersFormulaIdentifier}`, - ); - const formulaNumber = digester.digestHex().slice(32, 64); - - const formula = { - type: 'web-bundle', - bundle: bundleFormulaIdentifier, - powers: powersFormulaIdentifier, - }; - // Behold, recursion: - // eslint-disable-next-line no-use-before-define - const { value, formulaIdentifier } = await provideValueForNumberedFormula( - 'web-bundle', - formulaNumber, - formula, + const { value, formulaIdentifier } = await incarnateWebBundle( + powersFormulaIdentifier, + bundleFormulaIdentifier, ); if (webPageName !== undefined) { From 729d4aa8991febd37d774f1194fd9071024b561a Mon Sep 17 00:00:00 2001 From: kumavis Date: Sun, 18 Feb 2024 19:17:01 -1000 Subject: [PATCH 15/27] feat(daemon): add incarnateWorker --- packages/daemon/src/daemon.js | 46 +++++++++++++++++++++++++--------- packages/daemon/src/host.js | 25 +++++++++--------- packages/daemon/src/types.d.ts | 7 ++++++ 3 files changed, 53 insertions(+), 25 deletions(-) diff --git a/packages/daemon/src/daemon.js b/packages/daemon/src/daemon.js index 96bdd71e18..13db0b4ae0 100644 --- a/packages/daemon/src/daemon.js +++ b/packages/daemon/src/daemon.js @@ -346,6 +346,8 @@ const makeDaemonCore = async ( return { external, internal: undefined }; } else if (formula.type === 'lookup') { return makeControllerForLookup(formula.hub, formula.path, context); + } else if (formula.type === 'worker') { + return makeIdentifiedWorkerController(formulaNumber, context); } else if (formula.type === 'make-unconfined') { return makeControllerForUnconfinedPlugin( formula.worker, @@ -373,17 +375,13 @@ const makeDaemonCore = async ( context, ); } else if (formula.type === 'guest') { - const storeFormulaNumber = derive(formulaNumber, 'pet-store'); - const storeFormulaIdentifier = `pet-store:${storeFormulaNumber}`; - const workerFormulaNumber = derive(formulaNumber, 'worker'); - const workerFormulaIdentifier = `worker:${workerFormulaNumber}`; // Behold, recursion: // eslint-disable-next-line no-use-before-define return makeIdentifiedGuestController( formulaIdentifier, formula.host, - storeFormulaIdentifier, - workerFormulaIdentifier, + formula.petStore, + formula.worker, context, ); } else if (formula.type === 'web-bundle') { @@ -485,9 +483,7 @@ const makeDaemonCore = async ( context, ) => { const formulaIdentifier = `${formulaType}:${formulaNumber}`; - if (formulaType === 'worker') { - return makeIdentifiedWorkerController(formulaNumber, context); - } else if (formulaType === 'pet-inspector') { + if (formulaType === 'pet-inspector') { const storeFormulaNumber = derive(formulaNumber, 'pet-store'); const storeFormulaIdentifier = `pet-store:${storeFormulaNumber}`; // Behold, unavoidable forward-reference: @@ -503,6 +499,7 @@ const makeDaemonCore = async ( } else if ( [ 'endo', + 'worker', 'eval', 'readable-blob', 'make-unconfined', @@ -705,6 +702,20 @@ const makeDaemonCore = async ( ); }; + /** + * @returns {Promise<{ formulaIdentifier: string, value: import('./types').EndoWorker }>} + */ + const incarnateWorker = async () => { + const formulaNumber = await randomHex512(); + /** @type {import('./types.js').WorkerFormula} */ + const formula = { + type: 'worker', + }; + return /** @type {Promise<{ formulaIdentifier: string, value: import('./types').EndoWorker }>} */ ( + provideValueForNumberedFormula(formula.type, formulaNumber, formula) + ); + }; + /** * @param {string} endoFormulaIdentifier * @param {string} leastAuthorityFormulaIdentifier @@ -717,8 +728,11 @@ const makeDaemonCore = async ( specifiedWorkerFormulaIdentifier, ) => { const formulaNumber = await randomHex512(); - const workerFormulaIdentifier = - specifiedWorkerFormulaIdentifier || `worker:${await randomHex512()}`; + let workerFormulaIdentifier = specifiedWorkerFormulaIdentifier; + if (workerFormulaIdentifier === undefined) { + ({ formulaIdentifier: workerFormulaIdentifier } = + await incarnateWorker()); + } const inspectorFormulaNumber = derive(formulaNumber, 'pet-inspector'); const inspectorFormulaIdentifier = `pet-inspector:${inspectorFormulaNumber}`; // Note the pet store formula number derivation path: @@ -745,10 +759,16 @@ const makeDaemonCore = async ( */ const incarnateGuest = async hostHandleFormulaIdentifier => { const formulaNumber = await randomHex512(); + const storeFormulaNumber = derive(formulaNumber, 'pet-store'); + const storeFormulaIdentifier = `pet-store:${storeFormulaNumber}`; + const { formulaIdentifier: workerFormulaIdentifier } = + await incarnateWorker(); /** @type {import('./types.js').GuestFormula} */ const formula = { type: 'guest', host: hostHandleFormulaIdentifier, + petStore: storeFormulaIdentifier, + worker: workerFormulaIdentifier, }; return /** @type {Promise<{ formulaIdentifier: string, value: import('./types').EndoGuest }>} */ ( provideValueForNumberedFormula(formula.type, formulaNumber, formula) @@ -890,8 +910,9 @@ const makeDaemonCore = async ( const incarnateEndoBootstrap = async specifiedFormulaNumber => { const formulaNumber = specifiedFormulaNumber || (await randomHex512()); const endoFormulaIdentifier = `endo:${formulaNumber}`; - const defaultHostWorkerFormulaIdentifier = `worker:${await randomHex512()}`; + const { formulaIdentifier: defaultHostWorkerFormulaIdentifier } = + await incarnateWorker(); const { formulaIdentifier: leastAuthorityFormulaIdentifier } = await incarnateLeastAuthority(); @@ -927,6 +948,7 @@ const makeDaemonCore = async ( const makeIdentifiedHost = makeHostMaker({ provideValueForFormulaIdentifier, provideControllerForFormulaIdentifier, + incarnateWorker, incarnateHost, incarnateGuest, incarnateEval, diff --git a/packages/daemon/src/host.js b/packages/daemon/src/host.js index e72b58107e..b9cdf529b0 100644 --- a/packages/daemon/src/host.js +++ b/packages/daemon/src/host.js @@ -8,6 +8,7 @@ const { quote: q } = assert; export const makeHostMaker = ({ provideValueForFormulaIdentifier, provideControllerForFormulaIdentifier, + incarnateWorker, incarnateHost, incarnateGuest, incarnateEval, @@ -186,8 +187,8 @@ export const makeHostMaker = ({ } let workerFormulaIdentifier = identifyLocal(workerName); if (workerFormulaIdentifier === undefined) { - const workerId512 = await randomHex512(); - workerFormulaIdentifier = `worker:${workerId512}`; + ({ formulaIdentifier: workerFormulaIdentifier } = + await incarnateWorker()); assertPetName(workerName); await petStore.write(workerName, workerFormulaIdentifier); } else if (!workerFormulaIdentifier.startsWith('worker:')) { @@ -207,14 +208,15 @@ export const makeHostMaker = ({ if (workerName === 'MAIN') { return mainWorkerFormulaIdentifier; } else if (workerName === 'NEW') { - const workerId512 = await randomHex512(); - return `worker:${workerId512}`; + const { formulaIdentifier: workerFormulaIdentifier } = + await incarnateWorker(); + return workerFormulaIdentifier; } assertPetName(workerName); let workerFormulaIdentifier = identifyLocal(workerName); if (workerFormulaIdentifier === undefined) { - const workerId512 = await randomHex512(); - workerFormulaIdentifier = `worker:${workerId512}`; + ({ formulaIdentifier: workerFormulaIdentifier } = + await incarnateWorker()); assertPetName(workerName); await petStore.write(workerName, workerFormulaIdentifier); } @@ -370,19 +372,16 @@ export const makeHostMaker = ({ /** * @param {string} [petName] + * @returns {Promise} */ const makeWorker = async petName => { - const workerId512 = await randomHex512(); - const formulaIdentifier = `worker:${workerId512}`; + // Behold, recursion: + const { formulaIdentifier, value } = await incarnateWorker(); if (petName !== undefined) { assertPetName(petName); await petStore.write(petName, formulaIdentifier); } - return /** @type {Promise} */ ( - // Behold, recursion: - // eslint-disable-next-line no-use-before-define - provideValueForFormulaIdentifier(formulaIdentifier) - ); + return /** @type {import('./types.js').EndoWorker} */ (value); }; /** diff --git a/packages/daemon/src/types.d.ts b/packages/daemon/src/types.d.ts index 1f632b7974..8c452d42d9 100644 --- a/packages/daemon/src/types.d.ts +++ b/packages/daemon/src/types.d.ts @@ -64,6 +64,10 @@ type EndoFormula = { webPageJs?: string; }; +type WorkerFormula = { + type: 'worker'; +}; + type HostFormula = { type: 'host'; worker: string; @@ -76,6 +80,8 @@ type HostFormula = { type GuestFormula = { type: 'guest'; host: string; + petStore: string; + worker: string; }; type LeastAuthorityFormula = { @@ -140,6 +146,7 @@ type HandleFormula = { export type Formula = | EndoFormula + | WorkerFormula | HostFormula | GuestFormula | LeastAuthorityFormula From a108e7bfec5ccb7bc01301bafc5010179e0adfa6 Mon Sep 17 00:00:00 2001 From: kumavis Date: Sun, 18 Feb 2024 19:31:42 -1000 Subject: [PATCH 16/27] feat(daemon): add incarnatePetInspector --- packages/daemon/src/daemon.js | 36 +++++++++++++++++++++++----------- packages/daemon/src/types.d.ts | 8 +++++++- 2 files changed, 32 insertions(+), 12 deletions(-) diff --git a/packages/daemon/src/daemon.js b/packages/daemon/src/daemon.js index 13db0b4ae0..350bf55295 100644 --- a/packages/daemon/src/daemon.js +++ b/packages/daemon/src/daemon.js @@ -467,6 +467,11 @@ const makeDaemonCore = async ( }, }); return { external: leastAuthority, internal: undefined }; + } else if (formula.type === 'pet-inspector') { + // Behold, unavoidable forward-reference: + // eslint-disable-next-line no-use-before-define + const external = makePetStoreInspector(formula.petStore); + return { external, internal: undefined }; } else { throw new TypeError(`Invalid formula: ${q(formula)}`); } @@ -483,14 +488,7 @@ const makeDaemonCore = async ( context, ) => { const formulaIdentifier = `${formulaType}:${formulaNumber}`; - if (formulaType === 'pet-inspector') { - const storeFormulaNumber = derive(formulaNumber, 'pet-store'); - const storeFormulaIdentifier = `pet-store:${storeFormulaNumber}`; - // Behold, unavoidable forward-reference: - // eslint-disable-next-line no-use-before-define - const external = makePetStoreInspector(storeFormulaIdentifier); - return { external, internal: undefined }; - } else if (formulaType === 'pet-store') { + if (formulaType === 'pet-store') { const external = petStorePowers.makeIdentifiedPetStore( formulaNumber, assertPetName, @@ -510,6 +508,7 @@ const makeDaemonCore = async ( 'web-bundle', 'web-page-js', 'handle', + 'pet-inspector', ].includes(formulaType) ) { const formula = await persistencePowers.readFormula( @@ -733,12 +732,13 @@ const makeDaemonCore = async ( ({ formulaIdentifier: workerFormulaIdentifier } = await incarnateWorker()); } - const inspectorFormulaNumber = derive(formulaNumber, 'pet-inspector'); - const inspectorFormulaIdentifier = `pet-inspector:${inspectorFormulaNumber}`; // Note the pet store formula number derivation path: // root -> host -> inspector -> pet store - const storeFormulaNumber = derive(inspectorFormulaNumber, 'pet-store'); + const storeFormulaNumber = derive(formulaNumber, 'pet-store'); const storeFormulaIdentifier = `pet-store:${storeFormulaNumber}`; + const { formulaIdentifier: inspectorFormulaIdentifier } = + // eslint-disable-next-line no-use-before-define + await incarnatePetInspector(storeFormulaIdentifier); /** @type {import('./types.js').HostFormula} */ const formula = { type: 'host', @@ -903,6 +903,20 @@ const makeDaemonCore = async ( return provideValueForNumberedFormula(formula.type, formulaNumber, formula); }; + /** + * @param {string} petStoreFormulaIdentifier + * @returns {Promise<{ formulaIdentifier: string, value: unknown }>} + */ + const incarnatePetInspector = async petStoreFormulaIdentifier => { + const formulaNumber = await randomHex512(); + /** @type {import('./types.js').PetInspectorFormula} */ + const formula = { + type: 'pet-inspector', + petStore: petStoreFormulaIdentifier, + }; + return provideValueForNumberedFormula(formula.type, formulaNumber, formula); + }; + /** * @param {string} [specifiedFormulaNumber] * @returns {Promise<{ formulaIdentifier: string, value: import('./types').EndoBootstrap }>} diff --git a/packages/daemon/src/types.d.ts b/packages/daemon/src/types.d.ts index 8c452d42d9..194257d1c7 100644 --- a/packages/daemon/src/types.d.ts +++ b/packages/daemon/src/types.d.ts @@ -144,6 +144,11 @@ type HandleFormula = { target: string; }; +type PetInspectorFormula = { + type: 'pet-inspector'; + petStore: string; +}; + export type Formula = | EndoFormula | WorkerFormula @@ -156,7 +161,8 @@ export type Formula = | MakeUnconfinedFormula | MakeBundleFormula | WebBundleFormula - | HandleFormula; + | HandleFormula + | PetInspectorFormula; export type Label = { number: number; From 8cece42acfe27fb90c07f7741c9cf824cf41ae4e Mon Sep 17 00:00:00 2001 From: kumavis Date: Sun, 18 Feb 2024 19:44:39 -1000 Subject: [PATCH 17/27] feat(daemon): add incarnatePetStore --- packages/daemon/src/daemon.js | 45 ++++++++++++++++++++-------------- packages/daemon/src/types.d.ts | 7 +++++- 2 files changed, 32 insertions(+), 20 deletions(-) diff --git a/packages/daemon/src/daemon.js b/packages/daemon/src/daemon.js index 350bf55295..c919dcd7e2 100644 --- a/packages/daemon/src/daemon.js +++ b/packages/daemon/src/daemon.js @@ -66,12 +66,6 @@ const makeDaemonCore = async ( control: controlPowers, } = powers; const { randomHex512, makeSha512 } = cryptoPowers; - const derive = (...path) => { - const digester = makeSha512(); - digester.updateText(path.join(':')); - return digester.digestHex(); - }; - const contentStore = persistencePowers.makeContentSha512Store(); /** @type {Map>} */ @@ -467,6 +461,12 @@ const makeDaemonCore = async ( }, }); return { external: leastAuthority, internal: undefined }; + } else if (formula.type === 'pet-store') { + const external = petStorePowers.makeIdentifiedPetStore( + formulaNumber, + assertPetName, + ); + return { external, internal: undefined }; } else if (formula.type === 'pet-inspector') { // Behold, unavoidable forward-reference: // eslint-disable-next-line no-use-before-define @@ -488,13 +488,7 @@ const makeDaemonCore = async ( context, ) => { const formulaIdentifier = `${formulaType}:${formulaNumber}`; - if (formulaType === 'pet-store') { - const external = petStorePowers.makeIdentifiedPetStore( - formulaNumber, - assertPetName, - ); - return { external, internal: undefined }; - } else if ( + if ( [ 'endo', 'worker', @@ -509,6 +503,7 @@ const makeDaemonCore = async ( 'web-page-js', 'handle', 'pet-inspector', + 'pet-store', ].includes(formulaType) ) { const formula = await persistencePowers.readFormula( @@ -701,6 +696,20 @@ const makeDaemonCore = async ( ); }; + /** + * @returns {Promise<{ formulaIdentifier: string, value: import('./types').PetStore }>} + */ + const incarnatePetStore = async () => { + const formulaNumber = await randomHex512(); + /** @type {import('./types.js').PetStoreFormula} */ + const formula = { + type: 'pet-store', + }; + return /** @type {Promise<{ formulaIdentifier: string, value: import('./types').PetStore }>} */ ( + provideValueForNumberedFormula(formula.type, formulaNumber, formula) + ); + }; + /** * @returns {Promise<{ formulaIdentifier: string, value: import('./types').EndoWorker }>} */ @@ -732,10 +741,8 @@ const makeDaemonCore = async ( ({ formulaIdentifier: workerFormulaIdentifier } = await incarnateWorker()); } - // Note the pet store formula number derivation path: - // root -> host -> inspector -> pet store - const storeFormulaNumber = derive(formulaNumber, 'pet-store'); - const storeFormulaIdentifier = `pet-store:${storeFormulaNumber}`; + const { formulaIdentifier: storeFormulaIdentifier } = + await incarnatePetStore(); const { formulaIdentifier: inspectorFormulaIdentifier } = // eslint-disable-next-line no-use-before-define await incarnatePetInspector(storeFormulaIdentifier); @@ -759,8 +766,8 @@ const makeDaemonCore = async ( */ const incarnateGuest = async hostHandleFormulaIdentifier => { const formulaNumber = await randomHex512(); - const storeFormulaNumber = derive(formulaNumber, 'pet-store'); - const storeFormulaIdentifier = `pet-store:${storeFormulaNumber}`; + const { formulaIdentifier: storeFormulaIdentifier } = + await incarnatePetStore(); const { formulaIdentifier: workerFormulaIdentifier } = await incarnateWorker(); /** @type {import('./types.js').GuestFormula} */ diff --git a/packages/daemon/src/types.d.ts b/packages/daemon/src/types.d.ts index 194257d1c7..23b0fda63f 100644 --- a/packages/daemon/src/types.d.ts +++ b/packages/daemon/src/types.d.ts @@ -144,6 +144,10 @@ type HandleFormula = { target: string; }; +type PetStoreFormula = { + type: 'pet-store'; +}; + type PetInspectorFormula = { type: 'pet-inspector'; petStore: string; @@ -162,7 +166,8 @@ export type Formula = | MakeBundleFormula | WebBundleFormula | HandleFormula - | PetInspectorFormula; + | PetInspectorFormula + | PetStoreFormula; export type Label = { number: number; From 9fbbfb32cd305bc892af2dd64457cef7f27864c7 Mon Sep 17 00:00:00 2001 From: kumavis Date: Mon, 19 Feb 2024 05:39:30 -1000 Subject: [PATCH 18/27] feat(daemon): add incarnateLookup --- packages/daemon/src/daemon.js | 62 +++++++++++++++++++++++------------ packages/daemon/src/mail.js | 30 +++-------------- 2 files changed, 45 insertions(+), 47 deletions(-) diff --git a/packages/daemon/src/daemon.js b/packages/daemon/src/daemon.js index c919dcd7e2..f95af3906b 100644 --- a/packages/daemon/src/daemon.js +++ b/packages/daemon/src/daemon.js @@ -65,7 +65,7 @@ const makeDaemonCore = async ( persistence: persistencePowers, control: controlPowers, } = powers; - const { randomHex512, makeSha512 } = cryptoPowers; + const { randomHex512 } = cryptoPowers; const contentStore = persistencePowers.makeContentSha512Store(); /** @type {Map>} */ @@ -504,6 +504,7 @@ const makeDaemonCore = async ( 'handle', 'pet-inspector', 'pet-store', + 'lookup', ].includes(formulaType) ) { const formula = await persistencePowers.readFormula( @@ -646,26 +647,6 @@ const makeDaemonCore = async ( } }; - const makeContext = makeContextMaker({ - controllerForFormulaIdentifier, - provideControllerForFormulaIdentifier, - }); - - const makeMailbox = makeMailboxMaker({ - getFormulaIdentifierForRef, - provideValueForFormulaIdentifier, - provideControllerForFormulaIdentifier, - makeSha512, - provideValueForNumberedFormula, - provideControllerForFormulaIdentifierAndResolveHandle, - }); - - const makeIdentifiedGuestController = makeGuestMaker({ - provideValueForFormulaIdentifier, - provideControllerForFormulaIdentifierAndResolveHandle, - makeMailbox, - }); - /** * @returns {Promise<{ formulaIdentifier: string, value: import('./types').EndoGuest }>} */ @@ -809,6 +790,26 @@ const makeDaemonCore = async ( ); }; + /** + * @param {string} hubFormulaIdentifier + * A "naming hub" is an objected with a variadic lookup method. It includes + * objects such as guests and hosts. + * @param {string[]} petNamePath + * @returns {Promise<{ formulaIdentifier: string, value: unknown }>} + */ + const incarnateLookup = async (hubFormulaIdentifier, petNamePath) => { + const formulaNumber = await randomHex512(); + /** @type {import('./types.js').LookupFormula} */ + const formula = { + type: 'lookup', + hub: hubFormulaIdentifier, + path: petNamePath, + }; + return /** @type {Promise<{ formulaIdentifier: string, value: unknown }>} */ ( + provideValueForNumberedFormula(formula.type, formulaNumber, formula) + ); + }; + /** * @param {string} workerFormulaIdentifier * @param {string} powersFormulaIdentifiers @@ -966,6 +967,25 @@ const makeDaemonCore = async ( ); }; + const makeContext = makeContextMaker({ + controllerForFormulaIdentifier, + provideControllerForFormulaIdentifier, + }); + + const makeMailbox = makeMailboxMaker({ + incarnateLookup, + getFormulaIdentifierForRef, + provideValueForFormulaIdentifier, + provideControllerForFormulaIdentifier, + provideControllerForFormulaIdentifierAndResolveHandle, + }); + + const makeIdentifiedGuestController = makeGuestMaker({ + provideValueForFormulaIdentifier, + provideControllerForFormulaIdentifierAndResolveHandle, + makeMailbox, + }); + const makeIdentifiedHost = makeHostMaker({ provideValueForFormulaIdentifier, provideControllerForFormulaIdentifier, diff --git a/packages/daemon/src/mail.js b/packages/daemon/src/mail.js index 67027ef71c..e8a0974427 100644 --- a/packages/daemon/src/mail.js +++ b/packages/daemon/src/mail.js @@ -10,19 +10,17 @@ const { quote: q } = assert; /** * @param {object} args + * @param {(hubFormulaIdentifier: string, petNamePath: string[]) => Promise<{ formulaIdentifier: string, value: unknown }>} args.incarnateLookup * @param {import('./types.js').ProvideValueForFormulaIdentifier} args.provideValueForFormulaIdentifier * @param {import('./types.js').ProvideControllerForFormulaIdentifier} args.provideControllerForFormulaIdentifier * @param {import('./types.js').GetFormulaIdentifierForRef} args.getFormulaIdentifierForRef - * @param {import('./types.js').MakeSha512} args.makeSha512 - * @param {import('./types.js').ProvideValueForNumberedFormula} args.provideValueForNumberedFormula * @param {import('./types.js').ProvideControllerForFormulaIdentifierAndResolveHandle} args.provideControllerForFormulaIdentifierAndResolveHandle */ export const makeMailboxMaker = ({ + incarnateLookup, + getFormulaIdentifierForRef, provideValueForFormulaIdentifier, provideControllerForFormulaIdentifier, - getFormulaIdentifierForRef, - makeSha512, - provideValueForNumberedFormula, provideControllerForFormulaIdentifierAndResolveHandle, }) => { /** @@ -126,28 +124,8 @@ export const makeMailboxMaker = ({ /** @type {import('./types.js').Mail['provideLookupFormula']} */ const provideLookupFormula = async petNamePath => { - // The lookup formula identifier consists of the hash of the associated - // naming hub's formula identifier and the pet name path. - // A "naming hub" is an objected with a variadic lookup method. It includes - // objects such as guests and hosts. - const digester = makeSha512(); - digester.updateText(`${selfFormulaIdentifier},${petNamePath.join(',')}`); - const lookupFormulaNumber = digester.digestHex(); - // TODO:lookup Check if the lookup formula already exists in the store - /** @type {import('./types.js').LookupFormula} */ - const lookupFormula = { - /** @type {'lookup'} */ - type: 'lookup', - hub: selfFormulaIdentifier, - path: petNamePath, - }; - - return provideValueForNumberedFormula( - 'lookup', - lookupFormulaNumber, - lookupFormula, - ); + return incarnateLookup(selfFormulaIdentifier, petNamePath); }; /** From 21b5750211d0e5973dba6ca99b555ffbd46714f1 Mon Sep 17 00:00:00 2001 From: kumavis Date: Mon, 19 Feb 2024 05:45:54 -1000 Subject: [PATCH 19/27] fix(daemon): correct getWebPageBundlerFormula args --- packages/daemon/src/daemon.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/daemon/src/daemon.js b/packages/daemon/src/daemon.js index f95af3906b..832f3ee1d3 100644 --- a/packages/daemon/src/daemon.js +++ b/packages/daemon/src/daemon.js @@ -864,8 +864,8 @@ const makeDaemonCore = async ( } const formulaNumber = await randomHex512(); const formula = persistencePowers.getWebPageBundlerFormula( - powersFormulaIdentifier, workerFormulaIdentifier, + powersFormulaIdentifier, ); return provideValueForNumberedFormula(formula.type, formulaNumber, formula); }; From 0c6daf55a82a2389ed8c6f690ecf19c17e09ec76 Mon Sep 17 00:00:00 2001 From: kumavis Date: Mon, 19 Feb 2024 05:47:54 -1000 Subject: [PATCH 20/27] feat(daemon): expose all incarnate methods on daemonCore --- packages/daemon/src/daemon.js | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/packages/daemon/src/daemon.js b/packages/daemon/src/daemon.js index 832f3ee1d3..cfedc289e2 100644 --- a/packages/daemon/src/daemon.js +++ b/packages/daemon/src/daemon.js @@ -1125,8 +1125,20 @@ const makeDaemonCore = async ( const daemonCore = { provideValueForFormulaIdentifier, incarnateEndoBootstrap, + incarnateLeastAuthority, + incarnateHandle, + incarnatePetStore, + incarnateWorker, incarnateHost, + incarnateGuest, + incarnateEval, + incarnateLookup, + incarnateUnconfined, + incarnateReadableBlob, incarnateBundler, + incarnateBundle, + incarnateWebBundle, + incarnatePetInspector, }; return daemonCore; }; From 075d2be42892ad71044af6caa37e066755a74fb8 Mon Sep 17 00:00:00 2001 From: kumavis Date: Mon, 19 Feb 2024 15:28:57 -1000 Subject: [PATCH 21/27] refactor(daemon): DaemonicPersistencePowers provideRootNonce includes isNewlyCreated --- packages/daemon/src/daemon-node-powers.js | 11 +++-------- packages/daemon/src/daemon.js | 5 +++-- packages/daemon/src/types.d.ts | 3 +-- 3 files changed, 7 insertions(+), 12 deletions(-) diff --git a/packages/daemon/src/daemon-node-powers.js b/packages/daemon/src/daemon-node-powers.js index 66e53c6f9a..f0c7b55dd1 100644 --- a/packages/daemon/src/daemon-node-powers.js +++ b/packages/daemon/src/daemon-node-powers.js @@ -504,20 +504,16 @@ export const makeDaemonicPersistencePowers = ( await Promise.all([statePathP, cachePathP, ephemeralStatePathP]); }; - const isRootInitialized = async () => { - const noncePath = filePowers.joinPath(locator.statePath, 'nonce'); - const nonce = await filePowers.maybeReadFileText(noncePath); - return nonce !== undefined; - }; - const provideRootNonce = async () => { const noncePath = filePowers.joinPath(locator.statePath, 'nonce'); let nonce = await filePowers.maybeReadFileText(noncePath); + const isNewlyCreated = nonce === undefined; if (nonce === undefined) { nonce = await cryptoPowers.randomHex512(); await filePowers.writeFileText(noncePath, `${nonce}\n`); } - return nonce.trim(); + const rootNonce = nonce.trim(); + return { value: rootNonce, isNewlyCreated }; }; const makeContentSha512Store = () => { @@ -642,7 +638,6 @@ export const makeDaemonicPersistencePowers = ( return harden({ initializePersistence, provideRootNonce, - isRootInitialized, makeContentSha512Store, readFormula, writeFormula, diff --git a/packages/daemon/src/daemon.js b/packages/daemon/src/daemon.js index cfedc289e2..1ebc4e6473 100644 --- a/packages/daemon/src/daemon.js +++ b/packages/daemon/src/daemon.js @@ -1167,9 +1167,10 @@ const provideEndoBootstrap = async ( gracePeriodElapsed, }); - const isInitialized = await persistencePowers.isRootInitialized(); // Reading root nonce before isRootInitialized will cause isRootInitialized to be true. - const endoFormulaNumber = await persistencePowers.provideRootNonce(); + const { value: endoFormulaNumber, isNewlyCreated } = + await persistencePowers.provideRootNonce(); + const isInitialized = !isNewlyCreated; if (isInitialized) { const endoFormulaIdentifier = `endo:${endoFormulaNumber}`; return /** @type {Promise} */ ( diff --git a/packages/daemon/src/types.d.ts b/packages/daemon/src/types.d.ts index 23b0fda63f..ac2d572cf4 100644 --- a/packages/daemon/src/types.d.ts +++ b/packages/daemon/src/types.d.ts @@ -529,8 +529,7 @@ export type NetworkPowers = SocketPowers & { export type DaemonicPersistencePowers = { initializePersistence: () => Promise; - isRootInitialized: () => Promise; - provideRootNonce: () => Promise; + provideRootNonce: () => Promise<{ value: string; isNewlyCreated: boolean }>; makeContentSha512Store: () => { store: (readable: AsyncIterable) => Promise; fetch: (sha512: string) => EndoReadable; From d643f0c9a0c7f4361d4440c2c775261d2d73e725 Mon Sep 17 00:00:00 2001 From: kumavis Date: Mon, 19 Feb 2024 15:38:46 -1000 Subject: [PATCH 22/27] fix(daemon): rename EndoBootstrap type to FarEndoBootstrap --- packages/daemon/src/daemon.js | 10 +++++----- packages/daemon/src/types.d.ts | 6 +++--- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/packages/daemon/src/daemon.js b/packages/daemon/src/daemon.js index 1ebc4e6473..3e909db47c 100644 --- a/packages/daemon/src/daemon.js +++ b/packages/daemon/src/daemon.js @@ -405,7 +405,7 @@ const makeDaemonCore = async ( }, }; } else if (formula.type === 'endo') { - /** @type {import('./types.js').EndoBootstrap} */ + /** @type {import('./types.js').FarEndoBootstrap} */ const endoBootstrap = Far('Endo private facet', { // TODO for user named ping: async () => 'pong', @@ -927,7 +927,7 @@ const makeDaemonCore = async ( /** * @param {string} [specifiedFormulaNumber] - * @returns {Promise<{ formulaIdentifier: string, value: import('./types').EndoBootstrap }>} + * @returns {Promise<{ formulaIdentifier: string, value: import('./types').FarEndoBootstrap }>} */ const incarnateEndoBootstrap = async specifiedFormulaNumber => { const formulaNumber = specifiedFormulaNumber || (await randomHex512()); @@ -962,7 +962,7 @@ const makeDaemonCore = async ( leastAuthority: leastAuthorityFormulaIdentifier, webPageJs: webPageJsFormulaIdentifier, }; - return /** @type {Promise<{ formulaIdentifier: string, value: import('./types').EndoBootstrap }>} */ ( + return /** @type {Promise<{ formulaIdentifier: string, value: import('./types').FarEndoBootstrap }>} */ ( provideValueForNumberedFormula(formula.type, formulaNumber, formula) ); }; @@ -1151,7 +1151,7 @@ const makeDaemonCore = async ( * @param {(error: Error) => void} args.cancel * @param {number} args.gracePeriodMs * @param {Promise} args.gracePeriodElapsed - * @returns {Promise} + * @returns {Promise} */ const provideEndoBootstrap = async ( powers, @@ -1173,7 +1173,7 @@ const provideEndoBootstrap = async ( const isInitialized = !isNewlyCreated; if (isInitialized) { const endoFormulaIdentifier = `endo:${endoFormulaNumber}`; - return /** @type {Promise} */ ( + return /** @type {Promise} */ ( daemonCore.provideValueForFormulaIdentifier(endoFormulaIdentifier) ); } else { diff --git a/packages/daemon/src/types.d.ts b/packages/daemon/src/types.d.ts index ac2d572cf4..795cc7e1fd 100644 --- a/packages/daemon/src/types.d.ts +++ b/packages/daemon/src/types.d.ts @@ -448,7 +448,7 @@ export type EndoWebBundle = { powers: ERef; }; -export type EndoBootstrap = FarRef<{ +export type FarEndoBootstrap = FarRef<{ ping: () => Promise; terminate: () => Promise; host: () => Promise; @@ -513,13 +513,13 @@ export type NetworkPowers = SocketPowers & { cancelled: Promise; }) => Promise; makePrivatePathService: ( - endoBootstrap: EndoBootstrap, + endoBootstrap: FarEndoBootstrap, sockPath: string, cancelled: Promise, exitWithError: (error: Error) => void, ) => { started: Promise; stopped: Promise }; makePrivateHttpService: ( - endoBootstrap: EndoBootstrap, + endoBootstrap: FarEndoBootstrap, port: number, assignWebletPort: (portP: Promise) => void, cancelled: Promise, From 169c26cdeee49be742339b0a4da13dc1482da3d9 Mon Sep 17 00:00:00 2001 From: kumavis Date: Mon, 19 Feb 2024 16:09:38 -1000 Subject: [PATCH 23/27] doc(daemon): add doc for Handle and InternalHandle in types.d.ts --- packages/daemon/src/types.d.ts | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/packages/daemon/src/types.d.ts b/packages/daemon/src/types.d.ts index 795cc7e1fd..41e7af7da7 100644 --- a/packages/daemon/src/types.d.ts +++ b/packages/daemon/src/types.d.ts @@ -245,7 +245,20 @@ export type ProvideControllerForFormulaIdentifierAndResolveHandle = ( formulaIdentifier: string, ) => Promise; +/** + * A handle is used to create a pointer to a formula without exposing it directly. + * This is the external facet of the Handle and is safe to expose. This is used to + * provide an EndoGuest with a reference to its creator EndoHost. By using a handle + * that points to the host instead of giving a direct reference to the host, the + * guest does not get access to the functions of the host. This is the external + * facet of a handle. It directly exposes nothing. The handle's target is only + * exposed on the internal facet. + */ export interface Handle {} +/** + * This is the internal facet of a handle. It exposes the formula id that the + * handle points to. This should not be exposed outside of the endo daemon. + */ export interface InternalHandle { targetFormulaIdentifier: string; } From bdcb3ba0647dd9ffa80eefcc63a71a37dc63b30a Mon Sep 17 00:00:00 2001 From: kumavis Date: Mon, 19 Feb 2024 16:17:10 -1000 Subject: [PATCH 24/27] test(daemon): ensure guest cannot access HOST methods --- packages/daemon/test/test-endo.js | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/packages/daemon/test/test-endo.js b/packages/daemon/test/test-endo.js index c039e8999c..16a63f0108 100644 --- a/packages/daemon/test/test-endo.js +++ b/packages/daemon/test/test-endo.js @@ -1111,3 +1111,28 @@ test('list special names', async t => { t.assert(specialNames.every(name => name.toUpperCase() === name)); t.deepEqual([...specialNames, ...names], allNames); }); + +test('guest cannot access host methods', async t => { + const { promise: cancelled, reject: cancel } = makePromiseKit(); + t.teardown(() => cancel(Error('teardown'))); + + const locator = makeLocator('tmp', 'guest-cannot-host'); + + await start(locator); + t.teardown(() => stop(locator)); + + const { getBootstrap } = await makeEndoClient( + 'client', + locator.sockPath, + cancelled, + ); + const bootstrap = getBootstrap(); + const host = E(bootstrap).host(); + const guest = E(host).provideGuest('guest'); + const guestsHost = E(guest).lookup('HOST'); + await t.throwsAsync(() => E(guestsHost).lookup('SELF'), { + message: /target has no method "lookup"/u, + }); + const revealedTarget = await E.get(guestsHost).targetFormulaIdentifier; + t.is(revealedTarget, undefined); +}); From 1ae87177fcc3338f09cad86d500c86c9554117a3 Mon Sep 17 00:00:00 2001 From: kumavis Date: Mon, 19 Feb 2024 16:40:53 -1000 Subject: [PATCH 25/27] doc(daemon): improve doc introducing makeDaemonCore internals --- packages/daemon/src/daemon.js | 27 +++++++++++++++++++-------- 1 file changed, 19 insertions(+), 8 deletions(-) diff --git a/packages/daemon/src/daemon.js b/packages/daemon/src/daemon.js index 3e909db47c..6c530e1135 100644 --- a/packages/daemon/src/daemon.js +++ b/packages/daemon/src/daemon.js @@ -68,11 +68,26 @@ const makeDaemonCore = async ( const { randomHex512 } = cryptoPowers; const contentStore = persistencePowers.makeContentSha512Store(); - /** @type {Map>} */ + /** + * The two functions "provideValueForNumberedFormula" and "provideValueForFormulaIdentifier" + * share a responsibility for maintaining the memoization tables + * "controllerForFormulaIdentifier" and "formulaIdentifierForRef". + * "provideValueForNumberedFormula" is used for incarnating and persisting + * new formulas, whereas "provideValueForFormulaIdentifier" is used for + * reincarnating stored formulas. + */ + + /** + * Reverse look-up, for answering "what is my name for this near or far + * reference", and not for "what is my name for this promise". + * @type {Map} + */ const controllerForFormulaIdentifier = new Map(); - // Reverse look-up, for answering "what is my name for this near or far - // reference", and not for "what is my name for this promise". - /** @type {WeakMap} */ + /** + * Reverse look-up, for answering "what is my name for this near or far + * reference", and not for "what is my name for this promise". + * @type {WeakMap} + */ const formulaIdentifierForRef = new WeakMap(); const getFormulaIdentifierForRef = ref => formulaIdentifierForRef.get(ref); @@ -525,10 +540,6 @@ const makeDaemonCore = async ( } }; - // The two functions provideValueForFormula and provideValueForFormulaIdentifier - // share a responsibility for maintaining the memoization tables - // controllerForFormulaIdentifier and formulaIdentifierForRef, since the - // former bypasses the latter in order to avoid a round trip with disk. /** @type {import('./types.js').ProvideValueForNumberedFormula} */ const provideValueForNumberedFormula = async ( formulaType, From 77b1e681def325ee7ac088d2e7e09f63e4259160 Mon Sep 17 00:00:00 2001 From: Erik Marks Date: Mon, 19 Feb 2024 20:53:39 -0800 Subject: [PATCH 26/27] refactor(daemon): Update handle documentation and types --- packages/daemon/src/daemon-node-powers.js | 3 +-- packages/daemon/src/daemon.js | 7 +++---- packages/daemon/src/guest.js | 2 +- packages/daemon/src/host.js | 2 +- packages/daemon/src/types.d.ts | 9 ++++++--- 5 files changed, 12 insertions(+), 11 deletions(-) diff --git a/packages/daemon/src/daemon-node-powers.js b/packages/daemon/src/daemon-node-powers.js index f0c7b55dd1..3240d1ed8a 100644 --- a/packages/daemon/src/daemon-node-powers.js +++ b/packages/daemon/src/daemon-node-powers.js @@ -512,8 +512,7 @@ export const makeDaemonicPersistencePowers = ( nonce = await cryptoPowers.randomHex512(); await filePowers.writeFileText(noncePath, `${nonce}\n`); } - const rootNonce = nonce.trim(); - return { value: rootNonce, isNewlyCreated }; + return { rootNonce: nonce.trim(), isNewlyCreated }; }; const makeContentSha512Store = () => { diff --git a/packages/daemon/src/daemon.js b/packages/daemon/src/daemon.js index 6c530e1135..31a2cebafa 100644 --- a/packages/daemon/src/daemon.js +++ b/packages/daemon/src/daemon.js @@ -674,7 +674,7 @@ const makeDaemonCore = async ( /** * @param {string} targetFormulaIdentifier - * @returns {Promise<{ formulaIdentifier: string, value: import('./types').Handle }>} + * @returns {Promise<{ formulaIdentifier: string, value: import('./types').ExternalHandle }>} */ const incarnateHandle = async targetFormulaIdentifier => { const formulaNumber = await randomHex512(); @@ -683,7 +683,7 @@ const makeDaemonCore = async ( type: 'handle', target: targetFormulaIdentifier, }; - return /** @type {Promise<{ formulaIdentifier: string, value: import('./types').Handle }>} */ ( + return /** @type {Promise<{ formulaIdentifier: string, value: import('./types').ExternalHandle }>} */ ( provideValueForNumberedFormula(formula.type, formulaNumber, formula) ); }; @@ -1178,8 +1178,7 @@ const provideEndoBootstrap = async ( gracePeriodElapsed, }); - // Reading root nonce before isRootInitialized will cause isRootInitialized to be true. - const { value: endoFormulaNumber, isNewlyCreated } = + const { rootNonce: endoFormulaNumber, isNewlyCreated } = await persistencePowers.provideRootNonce(); const isInitialized = !isNewlyCreated; if (isInitialized) { diff --git a/packages/daemon/src/guest.js b/packages/daemon/src/guest.js index bdbe432589..7772652344 100644 --- a/packages/daemon/src/guest.js +++ b/packages/daemon/src/guest.js @@ -33,7 +33,7 @@ export const makeGuestMaker = ({ hostHandleFormulaIdentifier, ); const hostPrivateFacet = await hostController.internal; - const { respond: deliverToHost } = /** @type {any} */ (hostPrivateFacet); + const { respond: deliverToHost } = hostPrivateFacet; if (deliverToHost === undefined) { throw new Error( `panic: a host request function must exist for every host`, diff --git a/packages/daemon/src/host.js b/packages/daemon/src/host.js index b9cdf529b0..c0cf74bc42 100644 --- a/packages/daemon/src/host.js +++ b/packages/daemon/src/host.js @@ -81,7 +81,7 @@ export const makeHostMaker = ({ }); /** - * @returns {Promise<{ formulaIdentifier: string, value: import('./types').Handle }>} + * @returns {Promise<{ formulaIdentifier: string, value: import('./types').ExternalHandle }>} */ const makeNewHandleForSelf = () => { return incarnateHandle(hostFormulaIdentifier); diff --git a/packages/daemon/src/types.d.ts b/packages/daemon/src/types.d.ts index 41e7af7da7..c9e3b1b2e0 100644 --- a/packages/daemon/src/types.d.ts +++ b/packages/daemon/src/types.d.ts @@ -247,14 +247,14 @@ export type ProvideControllerForFormulaIdentifierAndResolveHandle = ( /** * A handle is used to create a pointer to a formula without exposing it directly. - * This is the external facet of the Handle and is safe to expose. This is used to + * This is the external facet of the handle and is safe to expose. This is used to * provide an EndoGuest with a reference to its creator EndoHost. By using a handle * that points to the host instead of giving a direct reference to the host, the * guest does not get access to the functions of the host. This is the external * facet of a handle. It directly exposes nothing. The handle's target is only * exposed on the internal facet. */ -export interface Handle {} +export interface ExternalHandle {} /** * This is the internal facet of a handle. It exposes the formula id that the * handle points to. This should not be exposed outside of the endo daemon. @@ -542,7 +542,10 @@ export type NetworkPowers = SocketPowers & { export type DaemonicPersistencePowers = { initializePersistence: () => Promise; - provideRootNonce: () => Promise<{ value: string; isNewlyCreated: boolean }>; + provideRootNonce: () => Promise<{ + rootNonce: string; + isNewlyCreated: boolean; + }>; makeContentSha512Store: () => { store: (readable: AsyncIterable) => Promise; fetch: (sha512: string) => EndoReadable; From e5d7082b934d19d0e86b1d2483db9346d7e59959 Mon Sep 17 00:00:00 2001 From: Erik Marks Date: Mon, 19 Feb 2024 21:01:46 -0800 Subject: [PATCH 27/27] fix(daemon): Restore 128-bit formula numbers for web bundles --- packages/daemon/src/daemon.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/packages/daemon/src/daemon.js b/packages/daemon/src/daemon.js index 31a2cebafa..b929dac396 100644 --- a/packages/daemon/src/daemon.js +++ b/packages/daemon/src/daemon.js @@ -912,7 +912,8 @@ const makeDaemonCore = async ( powersFormulaIdentifier, bundleFormulaIdentifier, ) => { - const formulaNumber = await randomHex512(); + // TODO use regular-length (512-bit) formula numbers for web bundles + const formulaNumber = (await randomHex512()).slice(32, 64); /** @type {import('./types.js').WebBundleFormula} */ const formula = { type: 'web-bundle',