Endpoint identity: a resource with per-path constructor interfaces - #31
Merged
Conversation
endpoint-options grows an optional identity — an Ed25519 signing/verifying pair carried as polymorph:webcrypto handles — so an embedder can provision and persist the endpoint's identity instead of bind minting a fresh one per call. Absent identity, bind behaves as before. The private key stays behind its webcrypto handle either way; the signature interface now appears on the world surface because the options record carries its handles, so the webcrypto package is vendored into wit/deps. bind validates a supplied pair — algorithm, sign permission, public-key shape, and a sign/verify possession probe of the halves against each other — and fails invalid-argument at bind, rather than letting a mismatched pair surface as handshake failures against every peer. The demo grows inject-identity: it mints the pair through its own webcrypto imports, hands the handles across the composition to bind, and requires the endpoint to report the injected public key as its endpoint-id. The matrix gains endpoint-identity-wasmtime-wasmtime, exercising injection on both roles over a relay echo. Fixes #29.
…mandatory Reshaped from the record form on review. `identity` is now an opaque resource owned by the package, with each provisioning path a separate constructor interface — `identity-generate` (fresh mint) and `identity-from-keys` (webcrypto handles) — so a world imports exactly the paths it needs, a deployment serves exactly the paths it implements, and a composition needing an unserved path fails at composition time. Consumers that never inject no longer see webcrypto types at all. Constructors validate, so an identity in hand is valid by construction and bind cannot fail for identity reasons. endpoint-options becomes a resource in the sibling options-resource style: the identity is the one mandatory input, expressed structurally as the constructor argument (borrowed — one identity configures any number of endpoints, which the consumed-record shape made impossible: key handles are not cloneable, so one pair could only ever reach one bind). Everything else is a setter defaulting to nothing granted; bind's relay-url and one-alpn requirements stay bind-time invalid-arguments, since both are recorded v0 narrowings rather than design rules, and a constructor argument would freeze that latitude into the interface. The demo constructs its identity explicitly on both paths — from-keys under --inject-identity, generate otherwise — and always asserts the endpoint reports the constructed identity's public key. The jco driver does the same through identity-generate.
# Conflicts: # endpoint-demo/wit/world.wit # host-wasmtime/src/bin/endpoint-demo.rs
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Fixes #29.
The ruling
Identity is an opaque resource, and each provisioning path is its own constructor interface:
identity— the resource (anendpoint-idgetter; valid by construction; reusable).identity-generate— fresh mint through the endpoint's crypto import. A deployment that requires provisioned identities withholds it.identity-from-keys— construction frompolymorph:webcrypto/signaturehandles: the embedder owns provisioning and persistence (platform-held, non-extractable keys included) and injects the pair. The only interface that names webcrypto types.from-keys, with no new endpoint surface.Worlds import exactly the construction paths they need; deployments export exactly the paths they serve; a composition needing an unserved path fails at
wac plug/instantiation time. Consumers that never inject see no webcrypto types at all — the record-in-options shape this PR originally carried leakedsignaturetypes into every endpoint consumer's bindings and transpile maps.endpoint-optionsbecomes a resource in the sibling options-resource style. The identity is the one design-mandatory input, expressed structurally as the constructor argument — borrowed, so one identity configures any number of endpoints (e.g. two binds with different ALPNs sharing an identity). The consumed-record shape made that literally impossible: webcrypto key handles are not cloneable, so one pair could only ever reach one bind. Everything else is a setter defaulting to nothing granted (add-alpnaccumulates; relay, UDP socket, webrtc all opt-in).bindconsumes the options; itsrelay-urland one-ALPN requirements stay bind-timeinvalid-arguments, because both are recorded v0 narrowings, not design rules — a constructor argument would freeze that latitude into the interface. Record→resource also moves options growth onto the semver-minor path (a new knob is a new method, not a breaking record change).Constructors validate —
from-keyschecks algorithm, sign permission, public-key shape, and runs a sign/verify possession probe (fixed message, discarded) — so anidentityin hand is a witness andbindcannot fail for identity reasons. The private key stays behind its webcrypto handle on every path.Coverage
The demo constructs its identity explicitly on both paths —
from-keysunder--inject-identity(webcrypto handles crossing the wac composition into the endpoint component),generateotherwise — and always assertsendpoint.id()equals the constructed identity's public key. Matrix rowendpoint-identity-wasmtime-wasmtimeexercises the injection path on both roles over a relay echo; every other endpoint row now exercisesidentity-generate. The jco driver (run-endpoint.mjs) constructs throughidentity-generatethe same way.Gates
fmt-check,clippy,validate-wit,test,probes, fullmatrix,bench— all green; the crypto boundary call-count budget is unmoved (generate performs exactly the keygen + public-key export bind used to perform; the possession probe's two extra calls exist only on the from-keys path).Boundary
Persistence mechanics stay out, per #29's scoping: a browser embedder structured-clones the non-extractable
CryptoKeyinto IndexedDB and rehydrates it host-side; the guest-visible storage surface remains polymorph-webcrypto#97, which composes here as just another constructor when it lands.