Skip to content

feat(ocapn): Minimum viable OCapN#2781

Closed
kumavis wants to merge 40 commits into
masterfrom
kumavis/ocapn-python-test-suite
Closed

feat(ocapn): Minimum viable OCapN#2781
kumavis wants to merge 40 commits into
masterfrom
kumavis/ocapn-python-test-suite

Conversation

@kumavis

@kumavis kumavis commented Apr 28, 2025

Copy link
Copy Markdown
Member

This PR is intended to land a work-in-progress implementation of the client, and is not ready to be published.

The implementation is a single vat single process (no comms vat).

  • Implements Passables
  • Remote calls + Pipelining
  • Third Party Handoffs
  • Eager promise subscription (lazy promise subscription to be explored later)
  • A tcp-testing-only netlayer (no tor netlayer yet)

This does not yet implement GC messages.

this was mostly built against this test suite https://github.com/ocapn/ocapn-test-suite
which lags behind the spec a bit. I expect dramatic spec changes to come, so I don't mind being behind spec at this point.

There is some implementation weirdness around use of pass-style due to a need for an ImmutableArrayBuffer. This implementation currently still uses mutable Uint8Arrays and thus some homespun passStyleOf checks. The intention is to replace these with exclusively real pass-style before publishing.

rebased on #2772

@kumavis
kumavis changed the base branch from master to kumavis/ocapn-improvements April 28, 2025 22:20
@kumavis
kumavis force-pushed the kumavis/ocapn-python-test-suite branch 3 times, most recently from c7275c8 to 8cc0aec Compare April 28, 2025 22:36
@kumavis
kumavis force-pushed the kumavis/ocapn-improvements branch from fe3ebc4 to 41f429e Compare April 29, 2025 17:43
@kumavis
kumavis changed the base branch from kumavis/ocapn-improvements to master April 29, 2025 19:42
@kumavis

kumavis commented Apr 29, 2025

Copy link
Copy Markdown
Member Author

When attempting to use @endo/pass-style's passStyleOf with selectors reified as registered symbols, I encountered an additional issue unrelated to symbols/selectors:

OCapN reifies ByteArrays as Uint8Arrays, but passStyleOf complains they are not hardened, even after a call to harden().

@erights i think you're working on support for something like an ImmutableArrayBuffer ? If you can point me at the code I can investigate using that. Until then I'm setting aside use of passStyleOf, temporarily working just with @endo/pass-style's PASS_STYLE. I have full intent to migrate to passStyleOf proper before marking this PR as ready for review.

@erights

erights commented Apr 29, 2025

Copy link
Copy Markdown
Contributor

OCapN reifies ByteArrays as Uint8Arrays, but passStyleOf complains they are not hardened, even after a call to harden().

Doh! I think I know what the problem is. Will investigate. Attn @kriskowal

@erights i think you're working on support for something like an ImmutableArrayBuffer

https://github.com/endojs/endo/blob/master/packages/immutable-arraybuffer/shim.js
#2400
#1538
#2414

I think one of #1538 or #2414 is redundant and should be closed in favor of the other one. I plan to look at these soon anyway, doing maintenance and bringing them up to date.

Would love any feedback!

@erights

erights commented Apr 29, 2025

Copy link
Copy Markdown
Contributor

I plan to look at these soon anyway, doing maintenance and bringing them up to date.

Especially since the proposal has reached stage 2.7 with no significant impediments to stage 3.0 (just further work on the test262 tests). We agreed at a recent Endo meeting that this was close enough for these dependent PRs to move ahead towards merging into endo master. (IIRC, you were there.)

@kumavis
kumavis force-pushed the kumavis/ocapn-python-test-suite branch 2 times, most recently from 14236e1 to db7bd65 Compare May 9, 2025 02:17
@kumavis
kumavis force-pushed the kumavis/ocapn-python-test-suite branch 3 times, most recently from 50ad13e to 0708a36 Compare May 19, 2025 21:36
@kumavis kumavis changed the title Feat(ocapn): Compatibility with OCapN Python test server Feat(ocapn): OCapN mvp May 23, 2025
@kumavis
kumavis requested a review from kriskowal May 23, 2025 20:15
@kumavis
kumavis force-pushed the kumavis/ocapn-python-test-suite branch from 933f2c4 to e1b3226 Compare May 23, 2025 20:15
@kumavis
kumavis marked this pull request as ready for review May 23, 2025 20:16
@kriskowal kriskowal changed the title Feat(ocapn): OCapN mvp feat(ocapn): Minimum viable OCapN May 23, 2025

@kriskowal kriskowal left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Looks like progress to me. I’m recommending that we capture issues for all the TODOs especially debts accrued by deferring alignment with @endo/pass-style.

Given that this doesn’t line up with the past, I would like it to line up with our best understanding of the anticipated future, so

  • please use ArrayBuffer for the representation of ByteArray (not Uint8Array) and also
  • use the name and representation of Symbol instead of Selector. I’ve made a note about what we want to do with methodNameForSymbol.

I am not sure what we’re going to do about your OCapNFar functions to reconcile with Far, but we must do something in pass-style and not bind that solution to OCapN. I don’t know what that solution is yet. But, makeExo and Far need to be sufficiently generic that they produce objects that work equally well with both CapTP and OCapN. I would be fine, for now, if you simply renamed your routine Far and moved it into your pass-style utility.

Comment on lines +32 to +34
const otherDir = slot[1] === '+' ? '-' : '+';
const revslot = `${slot[0]}${otherDir}${slot.slice(2)}`;
return revslot;

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

I have a fantasy where this function is simply:

const [table, pos] = slot;
return [table, -pos];

Comment thread packages/ocapn/src/client/index.js Outdated
Comment thread packages/ocapn/src/client/ocapn.js Outdated
Comment on lines +38 to +43
export const getSelectorName = selector => {
if (selector[PASS_STYLE] !== 'selector') {
throw Error(`Selector expected, got ${typeof selector}`);
}
return selector[Symbol.toStringTag];
};

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

I think we’re converging on (attn @erights). For context, the notion is to merge this OCapN implementation before pass-style is ready and to leave extensive notes with linked issues. The issue here is that this module will eventually be replaced with @endo/pass-style and so needs to converge on the same end state.

Suggested change
export const getSelectorName = selector => {
if (selector[PASS_STYLE] !== 'selector') {
throw Error(`Selector expected, got ${typeof selector}`);
}
return selector[Symbol.toStringTag];
};
export const methodNameForSymbol = symbol => {
if (symbol[PASS_STYLE] !== 'symbol') {
throw Error(`Selector expected, got ${typeof symbol}`);
}
tag = symbol[Symbol.toStringTag];
if (tag === '@@asyncIterator') {
return Symbol.asyncIterator;
} else if (tag === 'then') {
return Symbol.for('then'); // import { THEN } from "@endo/pass-style";
} else if (tag === 'constructor') {
return Symbol.for('constructor'); // import { CONSTRUCTOR } from "@endo/pass-style";
} else {
return tag;
}
};

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

I might punt adding special behavior for asyncIterator etc until I understand it better. also relates to how we handle OCapNFar.

import { PASS_STYLE } from '@endo/pass-style';

const { freeze, prototype: ObjectPrototype, create } = Object;

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Should capture a makeByteArray or byteArrayFrom here so we have a single place to change when lining up the representations.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

i tried implementing this and it started snaking out all over the code base. unsure what should be a ByteArray and what is fine being eg a Uint8Array. I'd rather revisit this with the ImmutableArrayBuffer shim.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Let’s think about what it will take to make ocapn usable in a shim-free environment. @erights

@erights erights Jun 10, 2025

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Let’s think about what it will take to make ocapn usable in a shim-free environment.

IMO, too hard, unless you want to wait for native implementations of the Immutable ArrayBuffer proposal. passStyleOf should only succeed on passables, which must therefore be hardened. The proposal would make either hardened Immutable ArrayBuffers or hardened frozen Uint8Arrays plausible choices for binding to ocapn ByteArray.

However, IMO, waiting for native implementations of Immutable ArrayBuffer would take too long. So the only reasonable choice IMO is to use the shim. The Shim's emulated Immutable ArrayBuffers can be made passable. But the shim cannot make Uint8Arrays passable, and has no reasonable way to emulate a frozen Uint8Array. So, for ocapn to proceed prior to native implementations, we gotta use the shim and bind Immutable ArrayBuffers to ocapn ByteArrays.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

This might be model.js or passable.js.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

this is used for testing and debugging. this is an otherwise unused direct reification of syrup to javascript. it is used by the ocapn client to log extra error info when it fails to parse an ocapn message.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

so i left it alone for now, but yeah "naming things"

Comment thread packages/ocapn/test/_util.js
Comment thread packages/ocapn/test/client.test.js Outdated
Comment thread packages/ocapn/test/codecs/descriptors.test.js Outdated
@kumavis
kumavis force-pushed the kumavis/ocapn-python-test-suite branch from 24ebd47 to f481aa8 Compare May 28, 2025 20:00
@kumavis
kumavis force-pushed the kumavis/ocapn-python-test-suite branch 2 times, most recently from 59a0d27 to 88f56ff Compare May 29, 2025 01:34
@kumavis
kumavis force-pushed the kumavis/ocapn-python-test-suite branch from 88f56ff to 770fa30 Compare May 29, 2025 02:22

@kriskowal kriskowal left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

I’ve made some notes. This meets my bar for a package that is not yet published and based on a still-wet specification and I’m eager to move forward quickly.

const makeSlotForValue = val => {
/** @type {CapTPSlot} */
let slot;
if (isPromise(val)) {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

passStyleOf(valu) === 'promise' will be more resilient to future changes.

}
seen.clear();
},
abort() {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Maybe just clear for this name.

Comment on lines +465 to +466
swissnumTable = new Map(),
giftTable = new Map(),

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Given that the caller can retain these options, is there an expectation that they can update them or observe changes to them after calling makeClient? If there is no such expectation, we should capture a snapshot and discard the option. If there is such an expectation, I’m weary about the semantics and the observability story.

@kumavis kumavis Sep 18, 2025

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

is there an expectation that they can update them or observe changes to them after calling makeClient

no. i've introduced them for tests, but they could also be part of a persistence system

we should capture a snapshot and discard the option.

seems fine. though we may inspect giftTable in tests

return `${location.transport}:${location.address}`;
};

const swissnumDecoder = new TextDecoder('ascii', { fatal: true });

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

I don’t believe that this usage is portable. Consider trivially hand-rolling this routine. I think it’s just something like String.fromCode(...array) as long as you assert that the values of the array are covered by the 0x7f mask.

import { PASS_STYLE } from '@endo/pass-style';

const { freeze, prototype: ObjectPrototype, create } = Object;

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Let’s think about what it will take to make ocapn usable in a shim-free environment. @erights

swissnumTable: makeDefaultSwissnumTable && makeDefaultSwissnumTable(),
});
const tcpNetlayer = await makeTcpNetLayer({ client });
client.registerNetlayer(tcpNetlayer);

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

I’m sure you’ve got an eye on how this integrates with the pet dæmon. We can add and remove netlayers at runtime? We probably ought to synchronously disconnect all living sessions for a netlayer when it’s unregistered.

@kumavis

kumavis commented Sep 18, 2025

Copy link
Copy Markdown
Member Author

replaced by #2962

@kumavis kumavis closed this Sep 18, 2025
@kumavis
kumavis deleted the kumavis/ocapn-python-test-suite branch May 1, 2026 06:48
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants