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

Using XS native Compartments for Agoric Vat Workers #400

Open
9 of 29 tasks
kriskowal opened this issue Aug 3, 2020 · 5 comments
Open
9 of 29 tasks

Using XS native Compartments for Agoric Vat Workers #400

kriskowal opened this issue Aug 3, 2020 · 5 comments
Assignees
Labels
devex developer experience kriskowal-review-2024-01 Issues that kriskowal wants to bring to the attention of the team for review as of January, 2024 metamask moddable p1 A temporary marker for highly motivated changes to be marked as priority 1 on for Agoric Engineering xsnap

Comments

@kriskowal
Copy link
Member

kriskowal commented Aug 3, 2020

[update 2024-04-29]

Agoric currently bundles contracts in a pre-compiled JSON envelope to avoid the runtime cost of loading and using Babel to transform ESM to a form that SES can digest. We could instead rely on the native XS ModuleSource behavior on XS if the native Compartment were sufficiently similar to the SES emulation of Compartment.

Enables #2116, #2117

It follows that we will be able to close when we have adequate parity with native XS for virtual module source on XS #1066


[update 2023-12-19]

Agoric currently bundles contracts in a pre-compiled JSON envelope to avoid the runtime cost of loading and using Babel to transform ESM to a form that SES can digest. We could instead rely on the native XS ModuleSource behavior on XS if the native Compartment were sufficiently similar to the SES emulation of Compartment.

  • establish 262 style tests that prove that we have adequate parity between XS Compartment and SES Comaprtment. This is likely to require changes to SES and a specialized thin variant of SES just for use on XS.
  • alter import bundle to take a dependency on @endo/static-module-record on Node.js but rely on the native ModuleSource constructor when running on XS. Differentiate these cases with a tag like endo in the package.json "exports" or "imports" property so that the SwingSet worker bootstrap for XS doesn’t entrain Babel when we generate that bundle.
  • alter compartment mapper to embed original sources instead of precompiled sources

[update 2020-01-21]

Agoric currently runs contracts in an XSnap worker using the SES shim for dynamic module-loading Compartment support. This works for now, and Moddable has made considerable progress toward making their native Compartment implementation a better alternative. To run a contract in a native Compartment, we would not need to use the bundleSource “censorship evasive transforms” that mitigate the weaknesses in the ses (hardened JavaScript shim) emulation of ESM (ECMAScript modules).

This issue stands open to track native compartment support. At time of writing, the ball is in our court. We need:

In the original description below, we are running the stopgap in production, and the longshot is not such a long shot anymore.


There are two avenues to running contract archives on Moddable’s XS. Both involve creating an XS binary for hosting dynamically-loaded modules from an archive (presumed Zip), including both CommonJS and ECMAScript modules. In both scenarios, this binary would implement Endo, ideally reüsing as much of its internals without modification as possible, so we have parity between running on Node.js with SES-Shim and running on XS without.

  1. Longshot: Fill out the XS Compartment API (C) such that it can incorporate modules at run-time.
  2. Stopgap: Emulate run-time modules using XS’s Compartment evaluator.

The Longshot is the most desirable long-term outcome. XS should in principle not need any part of the SES shim, since it’s a SES-only runtime. Also, our friends at Moddable are committed to implementing the Compartment specification, though the specification remains negotiable. To pursue this option, we may need to engage more closely with the XS project.

The Stopgap is possible (modulo risks) without major changes to XS, and largely reusing work we have already done on SES-Shim. We would need to implement the dynamic module loading feature as an xs-compartment-shim. This would reüse the transform-module Babel shim. We would need to factor some code out of the SES compartment-shim, presumably into the make-importer project Michael sketched.

The work break-down for the Longshot includes:

  1. XS Compartment would need to implement the StaticModuleRecord constructor, so ECMAScript modules can be parsed and analyzed.
  2. XS Compartment would need to implement importHook.
  3. On the first pass, importHook would need to be able to return a static module record provided by the above StaticModuleRecord and the Compartment and StaticModuleRecord can communicate behind the scenes with internal slots or weak maps to hide implementation details.
  4. On the second pass, importHook ould also support CommonJS, JSON, and potentially other modules by permitting the importHook to return implementations of static module record with the currently unspecified { imports, execute(exports, compartment, resolvedImports) } interface. This is possible as of feat(endo): Add CommonJS and JSON module support #397 on Node.js and would be necessary for parity, though we could constrain the transitive dependencies of contracts to be limited to the currently rare ESM format.
  5. XS Compartment would need to implement resolveHook. There is some wiggle room here since we actually just need every Compartment to use our particular resolveHook that works like Node.js's resolver, but where every full specifier is either package-local (starting with . or ..) or from another package. With this hook, resolve('./aux.js', './main.js') is ./aux.js, whereas resolve('../foo.js', './main.js') is an error since it escapes the package root. Folks who have implemented resolution for Node.js do not use this math since Node.js effectively executes within a single compartment where module specifiers are fully qualified and resolution may involve IO. This would be antithetical to compartmentalizing third-party packages like LavaMoat.
  6. XS Compartment and SES-shim Compartment would need to settle some minor differences or Endo would need to feature-test and work-around them. Notably SES-shim exposes import (which returns a promise) but not the importNow. This is likely a case where the specification and the SES shim need to change, since distinguishing importNow will no longer beuseful when the Compartment specification meets the new top-level-await.

The work break-down for Stopgap includes:

  1. We would need to factor the Compartment constructor out of SES-shim such that it can be used by Endo in XS without the lockdown shim.
  2. We would need a tool that turns an application that uses Node.js-style packages into a single namespace (so they can be precompiled into an XS binary) and a JavaScript" kernel that partitions the packages into child compartments and executes them in topological order. This is because the XS Compartment only supports pre-compiled ESM and does not support Node.js-style packaging. The “kernel” must be a single package of ECMASCript modules.
  3. The transitive dependencies of the XS binary application would need to all be ECMAScript modules. If we got the longshot, we could write a much smaller bootstrap and use Endo both for the application-runner (which depends on a CommonJS Zip library and Babel) and the application.
  4. We will need to port a Zip library to ESM.
  5. We will need to ensure that Babel can run under ESM, instead of the CommonJS projection of its ESM implementation.
@dckc
Copy link
Contributor

dckc commented Aug 3, 2020

... This is because the XS Compartment only supports pre-compiled ESM and does not support Node.js-style packaging. The “kernel” must be a single package of ECMASCript modules.

I wonder if that's quite true. I'm not sure what "Node.js-style packaging" means exactly. XS can import modules by reading .js from the filesystem at runtime. (I was in the past under the impression that it cannot, and I spread this misinformation around a bit, but I was wrong. IOU pointers to details)

I have incomplete understanding of importHook and a few other terms above, so I'm not sure how to share what I know about this topic. A meeting might be cost-effective.

@kriskowal
Copy link
Member Author

Thanks @dckc. Importing off the filesystem might be useful in some special cases. It would be useful to see an example of, say, writing a file to disk then importing it.

@dckc
Copy link
Contributor

dckc commented Aug 3, 2020

here's hoping I find time to cook up such an example. (I'm pretty sure I've done it before... maybe I still have it around...)

Meanwhile, for reference: fxLoadScript and fxLoadModule a few lines previous.

@dckc
Copy link
Contributor

dckc commented Aug 12, 2020

here's hoping I find time to cook up such an example. (I'm pretty sure I've done it before... maybe I still have it around...)

found it: https://gist.github.com/dckc/cbbd3e8469723b342cc90799ace7a287
from Jan 23 Agoric/agoric-sdk#426 (comment)

@kriskowal
Copy link
Member Author

The stopgap I described here has been working for some time now. Moddable has also recently made incredible strides toward making the longshot work too. I’m revising the title and description so that this issue continues to track native compartment support. #848 is related.

@kriskowal kriskowal changed the title Toward importing contract archives in Moddable’s XS Using XS native Compartments for Agoric Vat Workers Jan 21, 2022
@kriskowal kriskowal assigned kriskowal and unassigned dtribble Jan 10, 2024
@kriskowal kriskowal added the kriskowal-review-2024-01 Issues that kriskowal wants to bring to the attention of the team for review as of January, 2024 label Jan 10, 2024
@kriskowal kriskowal added devex developer experience p1 A temporary marker for highly motivated changes to be marked as priority 1 on for Agoric Engineering labels Jan 10, 2024
kriskowal added a commit that referenced this issue Jun 4, 2024
Refs: #400 and #2294

## Description

In this change, we carve and export `-lite.js` and `-parsers.js` out of
`import.js`, `archive.js`, and `import-archive.js`, as well revealing
`mapNodeModules` through `@endo/compartment-mapper/node-modules.js`
revealing hidden flexibility already present in the Compartment Mapper.
This allows the Compartment Mapper to mix and match “language behaviors”
with different workflows (e.g., using `import-parsers.js` with
`archive-lite.js` instead of `archive-parsers.js` generates archives
with original sources instead of precompiled sources.) We also decouple
the process of discovering the physical compartments such that
`node-modules.js` can be substituted for alternate compartment
exploration algorithms, e.g., combining a custom lockfile and package
cache from a specific package management solution.

### Security Considerations

Does not cross security considerations.

### Scaling Considerations

Exporting more narrowly scoped modules allows consumers to be avoid
entraining Babel in a greater variety of scenarios.

### Documentation Considerations

Evolution of the reference documentation should suffice, though another
pass at README.md to dig into these more surgical and composable APIs
may be worthwhile.

### Testing Considerations

The existing scenarios are extensive and fully cover the behaviors that
we’ve factored out. I expect #2294 tests to cover the new scenarios.

### Compatibility Considerations

This change takes great care to preserve the existing behavior of the
composite `import.js`, `archive.js`, and `import-archive.js` interfaces,
only revealing existing behaviors that were previously internal,
allowing more expressible scenarios.

### Upgrade Considerations

This change will not affect upgrade and paves a migration path forward
for preserving backward-compatibility for `bundleSource` and
`importBundle` as we draw closer to supporting XS native compartments.
kriskowal added a commit that referenced this issue Jun 4, 2024
Closes: #2295 
Refs: #400, #2252

## Description

This change adds a mode to the `bundle-source` command with the initial
flag `--no-transforms` that generates “endo zip base64” style bundles
without applying the module-to-program transform and SES shim censorship
evasion transforms, such that the original files on disk appear in the
zip file. This is a preparatory step, necessary for building test
artifacts, in advance of full support for this bundle style.

### Security Considerations

`bundle-source` is part of the Endo and Agoric toolkit and it, or its
surrogate, participate in the toolchain for generating content that can
be confined by Hardened JavaScript, but is not trusted by Hardened
JavaScript at runtime. It does however _currently_ run with all the
authority of the developer in their development environment and its
integrity must be carefully guarded.

### Scaling Considerations

No improvements expected at this time, but in pursuit of #400, it may be
possible to move the heavy and performance sensitive JavaScript
transform components from `bundle-source` to `import-bundle` and only
suffer the performance cost of these transforms on Node.js, where those
costs are more readily born by some runtimes. Precompiled bundles may
continue to be the preferred medium for deployment to the web, for
example.

### Documentation Considerations

We will need to advertise the `--no-transforms` flag eventually, since
there will be a period where it is advisable if not necessary to
generate contracts and caplets targeting the XS runtime.

### Testing Considerations

I have included a test that verifies the API behavior and manually run
the following to verify behavior for the CLI:

```sh
rm -rf bundles
yarn bundle-source --no-transforms --cache-json bundles demo/circular/a.js circular-a
rm -rf circular-a
mkdir -p circular-a
jq -r .endoZipBase64 bundles/bundle-circular-a.json | base64 -d > circular-a/circular-a.zip
(cd circular-a; unzip circular-a.zip)
jq . circular-a/compartment-map.json
# verifying the final module entires have parser: 'mjs'
```

### Compatibility Considerations

This flag is opt-in and breaks no prior behaviors. This introduces a new
entry to the build cache meta-data and may cause some bundles to be
regenerated one extra time after upgrading.

### Upgrade Considerations

This should not impact upgrade, though it participates in the greater
#400 story which will require xsnap upgrades to come to bear.
kriskowal added a commit that referenced this issue Jul 15, 2024
Refs: #400 #2251 

## Description

Pursuant to arriving at parity with XS, this change internally reörients
the SES module loader around the concept of a “module descriptor”. This
creates some clarity and removes some existing duplication of work for
“alias” module descriptors, and prepares the material for a richer
module descriptor schema to match those implemented by XS.

### Security Considerations

No changes.

### Scaling Considerations

No changes.

### Documentation Considerations

No changes.

### Testing Considerations

No changes.

### Compatibility Considerations

No changes.

### Upgrade Considerations

No changes.
kriskowal added a commit that referenced this issue Jul 16, 2024
Refs: #400
#2251

## Description

This change introduces support for XS-compatible module descriptors with
the discriminating properties `source` and `namespace`, including
support for loading a module from the parent compartment.

### Security Considerations

None.

### Scaling Considerations

None.

### Documentation Considerations

This change is additive.

### Testing Considerations

Covered.

### Compatibility Considerations

Some usage patterns are now deprecated but continue to be supported
without complaint.

### Upgrade Considerations

None.
kriskowal added a commit that referenced this issue Jul 16, 2024
Refs: #400

## Description

XS does not support `compartment.module`. Rather than emulate it, I
chose bring SES into closer alignment with XS module descriptors and use
those instead. The Compartment Mapper is the only place we use this
feature at the moment.

### Security Considerations

None.

### Scaling Considerations

None.

### Documentation Considerations

None.

### Testing Considerations

The existing Compartment Mapper tests exercise this path rigorously.

### Compatibility Considerations

The new version of Compartment Mapper will not work with an older
version of SES. Lerna will make sure the constraint gets updated when we
release.

### Upgrade Considerations

None.
kriskowal added a commit that referenced this issue Jul 19, 2024
Refs: #400, Agoric/agoric-sdk#9686

## Description

Endo’s zip bundle format and Rollup both support importing JSON modules.
This change adds JSON module support to Endo’s script bundle format. We
need this to have Rollup parity for Agoric Core Eval scripts and also to
unblock test262 for XS native compartment parity tests.

### Security Considerations

Obviates Rollup, increases legibility of script bundles.

### Scaling Considerations

None.

### Documentation Considerations

None. JSON module support is assumed. No one will notice unless it
doesn’t work.

### Testing Considerations

This change adds a JSON module to the existing bundle test fixture. This
also verifies parity with the Zip bundle format.

### Compatibility Considerations

This make Endo more compatible with Rollup, but Endo continues to not
support `import / with { type: "json" }`, which would be necessary for
parity with Node.js proper and newer bundlers.

### Upgrade Considerations

None.
kriskowal added a commit that referenced this issue Jul 19, 2024
Refs: #400

## Description

We’ve discovered that, to create an Endo script bundle that captures our
`ModuleSource` constructor shim, it will need to entrain Babel. With the
default build conditions, Babel entrains a `debug` package, which
entrains `node:tty`. So, it is necessary to add the `"browser"`
condition, which instead entrains a package with a JSON module (#2352).
This change threads arbitrary conditions (build tags) through
`bundle-source` to conveniently generate Endo script bundles with
different tags like `browser` or `xs`.

### Security Considerations

None.

### Scaling Considerations

None.

### Documentation Considerations

The new flags are advertised in the command’s usage display.

### Testing Considerations

- [ ] test scaffold and manual testing instructions

### Compatibility Considerations

None.

### Upgrade Considerations

None.
kriskowal added a commit that referenced this issue Jul 25, 2024
Refs: #400 

## Description

To reduce mismatched behavior between SES and XS, this change adds an
option (to be removed at the next major version) that changes the
compartment import method such that it returns a promise for an exports
namespace instead of a promise for a box containing the imports
namespace. This opts-in to the same thenable behavior as dynamic import
in the language.

### Security Considerations

With this option, we must be weary of thenable modules, as we must be
for thenable modules across the rest of the JavaScript ecosystem.

### Scaling Considerations

None.

### Documentation Considerations

This option will necessarily appear in all portable code examples on
hardenedjs.org once it is available.

### Testing Considerations

Tests have been adjusted to take advantage of the new option, except
legacy tests which remain to test the behavior without the option
enabled.

### Compatibility Considerations

This is a backward compatible change. Namespace boxes are hereafter
deprecated and we hope to remove this option on the next major release,
making the new behavior mandatory or at least opt-out.

### Upgrade Considerations

None.
kriskowal added a commit that referenced this issue Jul 30, 2024
Refs: #400

## Description

To converge with XS in the patterns accepted by SES for Compartment
construction, this change introduces a `__options__` property to the
first argument of the Compartment constructor that indicates that the
constructor accepts a single options bag argument and does not receive
separate endowments and module map arguments.

### Security Considerations

None.

### Scaling Considerations

None.

### Documentation Considerations

This change includes updates for NEWS (with migration instructions and a
notice of intent to break deprecated usage patterns) and README (to
encourage recommended usage). We intend to follow up with an update to
hardenedjs.org upon release.

### Testing Considerations

All tests have been updated to use the new constructor pattern except
those already marked as legacy, which remain to exercise support for the
deprecated usage patterns.

### Compatibility Considerations

There is a remote possibility that existing code uses `__options__` as
the name of an endowment and will now be misinterpreted. We do not
imagine any guest programs are in a position to influence their host
compartment’s construction.

### Upgrade Considerations

None.
kriskowal added a commit that referenced this issue Oct 21, 2024
…2591)

Refs: #2094

## Description

Elaborates failing tests for a known defect of ModuleSource, encountered
in the development of #400
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
devex developer experience kriskowal-review-2024-01 Issues that kriskowal wants to bring to the attention of the team for review as of January, 2024 metamask moddable p1 A temporary marker for highly motivated changes to be marked as priority 1 on for Agoric Engineering xsnap
Projects
None yet
Development

No branches or pull requests

3 participants