Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(daemon): store endo bootstrap and default host formulas #2089

Merged
merged 27 commits into from
Feb 20, 2024

Conversation

kumavis
Copy link
Member

@kumavis kumavis commented Feb 17, 2024

  • Factors out endo configuration ( eg default host formulaIdentifier ) from newly named daemonCore.
  • Introduces incarnateXyz methods to be used when creating a new thing for the first time.
  • Persists all formulas and requires them to be incarnated before they can be reified via provideValueForFormulaId
  • Adds Handles as a generic pointer to another formula, where the pointer is exposed on the internal facet only. This is currently used to give a guest a reference to the host without exposing host endowments. mail uses provideControllerForFormulaIdentifierAndResolveHandle to resolve the handle.

Updates: #1993
Closes: #2087

@kumavis kumavis force-pushed the kumavis-daemon-handle branch from 30bbfbc to 9519f27 Compare February 18, 2024 04:48
@kumavis kumavis force-pushed the kumavis-daemon-handle branch from 60046db to d598be7 Compare February 18, 2024 08:12
@kumavis kumavis force-pushed the kumavis-daemon-handle branch 3 times, most recently from 0340e35 to 252853e Compare February 18, 2024 23:07
@kumavis kumavis force-pushed the kumavis-daemon-handle branch from 252853e to c5a001c Compare February 18, 2024 23:18
@kumavis
Copy link
Member Author

kumavis commented Feb 18, 2024

once witnessed an unreproduceable EPIPE in CI 🤔
https://github.com/endojs/endo/actions/runs/7952406406/job/21706961986?pr=2089

@kumavis kumavis force-pushed the kumavis-daemon-handle branch from 19a7614 to 0bd689d Compare February 19, 2024 00:34
@kumavis kumavis force-pushed the kumavis-daemon-handle branch from c00b093 to b391c25 Compare February 19, 2024 04:10
@kumavis kumavis force-pushed the kumavis-daemon-handle branch from d110daf to 729d4aa Compare February 19, 2024 05:17
@kumavis
Copy link
Member Author

kumavis commented Feb 19, 2024

@kumavis kumavis marked this pull request as ready for review February 19, 2024 07:56
Copy link
Contributor

@rekmarks rekmarks left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks promising!

packages/daemon/src/daemon-node-powers.js Outdated Show resolved Hide resolved
packages/daemon/src/types.d.ts Outdated Show resolved Hide resolved
packages/daemon/src/types.d.ts Outdated Show resolved Hide resolved
packages/daemon/src/mail.js Show resolved Hide resolved
packages/daemon/src/guest.js Outdated Show resolved Hide resolved
packages/daemon/src/guest.js Show resolved Hide resolved
Comment on lines +62 to +64
host: string;
leastAuthority: string;
webPageJs?: string;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It would be neat to document what the string properties on all of our formulas are supposed to be. Names? Identifiers if the real thing? Identifiers of handles?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

how to make subtypes of string? so you can't give it a string unless you cast it to a formulaId? or not a good idea?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah, I don't think it's worth doing anything with template literal types here (especially in light of #2088). I just meant documenting what they're supposed to be on the interfaces would be nice.

I think we're OK for now since that information is there when the formulas are instantiated, so I won't block on it.

packages/daemon/src/daemon.js Show resolved Hide resolved
packages/daemon/src/daemon.js Show resolved Hide resolved
packages/daemon/src/daemon.js Show resolved Hide resolved
@rekmarks
Copy link
Contributor

One more question: what's the difference between incarnating something and reifying it? We should perhaps document this.

@kumavis kumavis force-pushed the kumavis-daemon-handle branch from 35ece3f to 169c26c Compare February 20, 2024 02:09
@kumavis
Copy link
Member Author

kumavis commented Feb 20, 2024

One more question: what's the difference between incarnating something and reifying it? We should perhaps document this.

this PR introduces "incarnate" as a verb to mean to create a new thing for the first time. it follows that we can use "reincarnate" to reify things. fun. But would not be surprised if we land on different language.

Copy link
Contributor

@rekmarks rekmarks left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM!

Pushed up some minor changes in 77b1e68. Fixed broken web-bundle formula numbers in ba4c257.

@rekmarks rekmarks force-pushed the kumavis-daemon-handle branch from ba4c257 to e5d7082 Compare February 20, 2024 08:27
@kumavis
Copy link
Member Author

kumavis commented Feb 20, 2024

thanks for the improvements/

@kumavis kumavis merged commit e9f7049 into master Feb 20, 2024
14 checks passed
@kumavis kumavis deleted the kumavis-daemon-handle branch February 20, 2024 17:45
assertPetName,
);
return { external, internal: undefined };
} else if (formula.type === 'pet-inspector') {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We could probably turn this would-be-switch block into a JavaScript map from type to behavior now.

rekmarks added a commit that referenced this pull request Feb 21, 2024
Synchronizes the host's `evaluate()` method by delegating all
incarnations to the daemon's `incarnateEval()`. Also synchronizes
`incarnateLookup()`, and removes `provideLookupFormula()` from the
mailbox, which was only a thin wrapper anyway.

The path taken introduces new incarnation semantics and design constraints on
the daemon and its dependents. #2089 introduced the notion of "incarnating"
values. The `incarnateX() methods are responsible for creating formula JSON
objects and calling `provideValueForNumberedFormula` to reify their values
for the first time. Synchronizing dependencies between `incarnateX` methods
is feasible by means of a lock, but difficulties arise when formulas are
incarnated outside the daemon, such as in the host. Coordinating a lock
between two different modules will increase the risks of datalocks at
runtime.

To avoid this, the implementation delegates all incarnations necessary
for `evaluate()` to its dependent `incarnateEval()`. In essence, it is
the consumer's responsibility to specify all necessary incarnations to
the relevant incarnation method, which is then responsible for carrying
them out. Yet, the story is complicated by pet names being mostly
abstracted away from the daemon. For example, pet names must be
associated with their respective formula identifiers the moment that
those identifiers are observable to the consumer. As part of this
process, the pet store must write the name to formula id mapping to
disk. To handle sych asynchronous side effects, the implementation
introduces a notion of "hooks" to `incarnateEval()`, with the intention
of spreading this to other incarnation methods as necessary. These hooks
receive as an argument all formula identifiers created by the
incarnation, and are executed under the formula graph synchronization lock.
This will surface IO errors to the consumer, and help us uphold the principle
of "death before confusion".
rekmarks added a commit that referenced this pull request Feb 21, 2024
Synchronizes the host's `evaluate()` method by delegating all
incarnations to the daemon's `incarnateEval()`. Also synchronizes
`incarnateLookup()`, and removes `provideLookupFormula()` from the
mailbox, which was only a thin wrapper anyway.

The path taken introduces new incarnation semantics and design constraints on
the daemon and its dependents. #2089 introduced the notion of "incarnating"
values. The `incarnateX() methods are responsible for creating formula JSON
objects and calling `provideValueForNumberedFormula` to reify their values
for the first time. Synchronizing dependencies between `incarnateX` methods
is feasible by means of a lock, but difficulties arise when formulas are
incarnated outside the daemon, such as in the host. Coordinating a lock
between two different modules will increase the risks of datalocks at
runtime.

To avoid datalocks, the implementation delegates all incarnations necessary
for `evaluate()` to its dependent `incarnateEval()`. In essence, it is
the consumer's responsibility to specify all necessary incarnations to
the relevant incarnation method, which is then responsible for carrying
them out. Yet, the story is complicated by pet names being mostly
abstracted away from the daemon. For example, pet names must be
associated with their respective formula identifiers the moment that
those identifiers are observable to the consumer. As part of this
process, the pet store must write the name to formula id mapping to
disk. To handle sych asynchronous side effects, the implementation
introduces a notion of "hooks" to `incarnateEval()`, with the intention
of spreading this to other incarnation methods as necessary. These hooks
receive as an argument all formula identifiers created by the
incarnation, and are executed under the formula graph synchronization lock.
This will surface IO errors to the consumer, and help us uphold the
principle of "death before confusion".

Also of note, `provideValueForNumberedFormula` has been modified such
that the formula is written to disk _after_ the controller has been
constructed. This is critical in order to serialize in-memory formula
graph mutations. Since it's possible for incarnation to fail after the
controller has been created, we should consider adding cleanup callbacks
to incarnation hooks.
rekmarks added a commit that referenced this pull request Feb 22, 2024
Synchronizes the host's `evaluate()` method by delegating all
incarnations to the daemon via `incarnateEval()`. The latter is
responsible for incarnating its dependents as necessary, and for the
generation of their formula numbers. To facilitate this, the synchronous
methods `incarnateLookupSync()` and `incarnateWorkerSync()` have been
added. These methods synchronously mutate the formula graph, and return
promises that resolve when the formulas have been written to disk. The
result is that the formula graph is mutated within a single turn of the
event loop.

To achieve this, the implementation introduces new constraints on the
daemon and its dependents. #2089 introduced the notion of "incarnating"
values. By the current definition, incarnating a value consists of the
following steps:
1. Collecting dependencies (async)
  a. Generating requisite formula numbers (async)
      - We use the asynchronous signature of `crypto.randomBytes` to do
      this for performance reasons.
  b. Incarnating any dependent values (recursion!)
2. Updating the in-memory formula graph (sync)
3. Writing the resulting formula to disk (async)
4. Reifiying the resulting value (async)

In order to make formula graph mutations mutually exclusive, we
introduce a "formula graph mutex" under which step 1 must be performed.
This mutex is currently only used by `incarnateEval()`, and must be
expanded to its sibling methods in the future.

`incarnateEval()` also introduces the notion of "incarnation hooks" to
the codebase. Originators of incarnations that are exogenous to the
daemon may themselves have asynchronous work perform. For example,
`petName -> formulaIdentifier` mappings are the responsibility of the
host and its pet store, and pet names must be associated with their
respective formula identifiers the moment that those identifiers are
observable to the consumer. To handle sych asynchronous side effects,
the implementation introduces a notion of "hooks" to `incarnateEval()`,
with the intention of spreading this to other incarnation methods as
necessary. These hooks receive as an argument all formula identifiers
created by the incarnation, and are executed under the formula graph
mutex. This will surface IO errors to the consumer, and help us uphold
the principle of "death before confusion".

Also of note, `provideValueForNumberedFormula` has been modified such
that the formula is written to disk _after_ the controller has been
constructed. This is critical in order to synchronize formula graph
mutations.

Finally, it appears that the implementation incidentally fixed #2021.
We may still wish to adopt the more robust solution proposed in that
issue.
rekmarks added a commit that referenced this pull request Feb 22, 2024
Synchronizes the host's `evaluate()` method by delegating all
incarnations to the daemon via `incarnateEval()`. The latter is
responsible for incarnating its dependents as necessary, and for the
generation of their formula numbers. To facilitate this, the synchronous
methods `incarnateLookupSync()` and `incarnateWorkerSync()` have been
added. These methods synchronously mutate the formula graph, and return
promises that resolve when the formulas have been written to disk. The
result is that the formula graph is mutated within a single turn of the
event loop.

To achieve this, the implementation introduces new constraints on the
daemon and its dependents. #2089 introduced the notion of "incarnating"
values. By the current definition, incarnating a value consists of the
following steps:
1. Collecting dependencies (async)
  a. Generating requisite formula numbers (async)
      - We use the asynchronous signature of `crypto.randomBytes` to do
      this for performance reasons.
  b. Incarnating any dependent values (recursion!)
2. Updating the in-memory formula graph (sync)
3. Writing the resulting formula to disk (async)
4. Reifiying the resulting value (async)

In order to make formula graph mutations mutually exclusive, we
introduce a "formula graph mutex" under which step 1 must be performed.
This mutex is currently only used by `incarnateEval()`, and must be
expanded to its sibling methods in the future.

`incarnateEval()` also introduces the notion of "incarnation hooks" to
the codebase. Originators of incarnations that are exogenous to the
daemon may themselves have asynchronous work perform. For example,
`petName -> formulaIdentifier` mappings are the responsibility of the
host and its pet store, and pet names must be associated with their
respective formula identifiers the moment that those identifiers are
observable to the consumer. To handle sych asynchronous side effects,
the implementation introduces a notion of "hooks" to `incarnateEval()`,
with the intention of spreading this to other incarnation methods as
necessary. These hooks receive as an argument all formula identifiers
created by the incarnation, and are executed under the formula graph
mutex. This will surface IO errors to the consumer, and help us uphold
the principle of "death before confusion".

Also of note, `provideValueForNumberedFormula` has been modified such
that the formula is written to disk _after_ the controller has been
constructed. This is critical in order to synchronize formula graph
mutations.

Finally, it appears that the implementation incidentally fixed #2021.
We may still wish to adopt the more robust solution proposed in that
issue.
rekmarks added a commit that referenced this pull request Feb 22, 2024
Synchronizes the host's `evaluate()` method by delegating all
incarnations to the daemon via `incarnateEval()`. The latter is
responsible for incarnating its dependents as necessary, and for the
generation of their formula numbers. To facilitate this, the synchronous
methods `incarnateLookupSync()` and `incarnateWorkerSync()` have been
added. These methods synchronously mutate the formula graph, and return
promises that resolve when the formulas have been written to disk. The
result is that the formula graph is mutated within a single turn of the
event loop.

To achieve this, the implementation introduces new constraints on the
daemon and its dependents. #2089 introduced the notion of "incarnating"
values. By the current definition, incarnating a value consists of the
following steps:
1. Collecting dependencies (async)
  a. Generating requisite formula numbers (async)
      - We use the asynchronous signature of `crypto.randomBytes` to do
      this for performance reasons.
  b. Incarnating any dependent values (recursion!)
2. Updating the in-memory formula graph (sync)
3. Writing the resulting formula to disk (async)
4. Reifiying the resulting value (async)

In order to make formula graph mutations mutually exclusive, we
introduce a "formula graph mutex" under which step 1 must be performed.
This mutex is currently only used by `incarnateEval()`, and must be
expanded to its sibling methods in the future.

`incarnateEval()` also introduces the notion of "incarnation hooks" to
the codebase. Originators of incarnations that are exogenous to the
daemon may themselves have asynchronous work perform. For example,
`petName -> formulaIdentifier` mappings are the responsibility of the
host and its pet store, and pet names must be associated with their
respective formula identifiers the moment that those identifiers are
observable to the consumer. To handle sych asynchronous side effects,
the implementation introduces a notion of "hooks" to `incarnateEval()`,
with the intention of spreading this to other incarnation methods as
necessary. These hooks receive as an argument all formula identifiers
created by the incarnation, and are executed under the formula graph
mutex. This will surface IO errors to the consumer, and help us uphold
the principle of "death before confusion".

Also of note, `provideValueForNumberedFormula` has been modified such
that the formula is written to disk _after_ the controller has been
constructed. This is critical in order to synchronize formula graph
mutations.

Finally, it appears that the implementation incidentally fixed #2021.
We may still wish to adopt the more robust solution proposed in that
issue.
rekmarks added a commit that referenced this pull request Feb 22, 2024
Synchronizes the host's `evaluate()` method by delegating all
incarnations to the daemon via `incarnateEval()`. The latter is
responsible for incarnating its dependents as necessary, and for the
generation of their formula numbers. To facilitate this, the synchronous
methods `incarnateLookupSync()` and `incarnateWorkerSync()` have been
added. These methods synchronously mutate the formula graph, and return
promises that resolve when the formulas have been written to disk. The
result is that the formula graph is mutated within a single turn of the
event loop.

To achieve this, the implementation introduces new constraints on the
daemon and its dependents. #2089 introduced the notion of "incarnating"
values. By the current definition, incarnating a value consists of the
following steps:
1. Collecting dependencies (async)
  a. Generating requisite formula numbers (async)
      - We use the asynchronous signature of `crypto.randomBytes` to do
      this for performance reasons.
  b. Incarnating any dependent values (recursion!)
2. Updating the in-memory formula graph (sync)
3. Writing the resulting formula to disk (async)
4. Reifiying the resulting value (async)

In order to make formula graph mutations mutually exclusive, we
introduce a "formula graph mutex" under which step 1 must be performed.
This mutex is currently only used by `incarnateEval()`, and must be
expanded to its sibling methods in the future.

`incarnateEval()` also introduces the notion of "incarnation hooks" to
the codebase. Originators of incarnations that are exogenous to the
daemon may themselves have asynchronous work perform. For example,
`petName -> formulaIdentifier` mappings are the responsibility of the
host and its pet store, and pet names must be associated with their
respective formula identifiers the moment that those identifiers are
observable to the consumer. To handle sych asynchronous side effects,
the implementation introduces a notion of "hooks" to `incarnateEval()`,
with the intention of spreading this to other incarnation methods as
necessary. These hooks receive as an argument all formula identifiers
created by the incarnation, and are executed under the formula graph
mutex. This will surface IO errors to the consumer, and help us uphold
the principle of "death before confusion".

Also of note, `provideValueForNumberedFormula` has been modified such
that the formula is written to disk _after_ the controller has been
constructed. This is critical in order to synchronize formula graph
mutations.

Finally, it appears that the implementation incidentally fixed #2021.
We may still wish to adopt the more robust solution proposed in that
issue.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

daemon: All formulas should be associated with a JSON file
3 participants