-
Notifications
You must be signed in to change notification settings - Fork 6
feat: Invite over internet #1254
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Open
RangerMauve
wants to merge
107
commits into
main
Choose a base branch
from
feat/invite-over-internet
base: main
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
Changes from 8 commits
Commits
Show all changes
107 commits
Select commit
Hold shift + click to select a range
e8646fd
feat: RemoteDiscovery module with Hyperswarm
2f14b72
fix: Connect to correct pear in remote discovery test
4255361
fix: Update bogon for hyperdht filtering
ad6c0be
test: Fix peer detection in remote discovery
f4a8c84
feat: Connect RemoteDiscovery to member API `inviteOverInternet` func…
8e2e3ac
feat: Simplify MemberAPI constructor parameters
039689f
test: Check member API invite over internet properties
b88f0fd
feat: RPC for redeeming invite over internet
f1f0767
feat: e2e invite over the internet
4675bdd
feat: Use ephemeral keys for hyperswarm, give real key on handshake
394facd
fix: MemberAPI.pendingInternetInvites should return URLs not invite IDs
01215f2
fix: Adjust remote discovery tests to use swarm key for connecting
ad82754
fix: Set own device ID for server tests since it is needed for projec…
54259c4
test: Check that remote connections can send data after connecting
50eaffb
fix: Use swarm public key to join project over internet
98b3350
chore: Clean handle redeem in member api
20203b0
fix: Pause swarm stream after doing initial handshake to avoid packet…
2b227d6
feat: Do not trust RPC until manual approval for swarm peers
968a737
chore: Add error classes for invite over internet code
d112c76
test: Check for error paths in RemoteDiscovery
f0026ac
fix: Detect existing connections
186d55b
test: Check handling of errors during invite over internet process
91b6614
chore: Fix formatting in manager constructor
225cb40
feat: Add new project table for pending internet invites
adccdcf
feat: Pending invites API with test
925e48a
fix: All ready resource methods can be optiona
dcc7c05
chore: Tidy remote discovery JSdoc types
35c4296
feat: Add persistent pending invites to member API
b789b56
fix: Type cast members events in tests due to ReadyResource change
e78ca86
test: Check for persistence in member API
e0264b1
feat: Length prefix remote discovery handshake buffer
6bc83d1
chore: merge with main
e9b7426
chore: Fix ready-resource removal issues
5cde04b
feat: Determenistic swarm key generation
922761a
fix: Use array.sort instead of array.toSorted for node18 support
f79759d
fix: log error when error is unknown type
138ba97
fix: Delete null values from loaded pending invite before passing to …
6c0122c
test: invite over the internet after invitor reloads app
3434e8c
feat: Store pending invites at the mapeo manager level
2d21931
Merge branch 'main' into feat/invite-over-internet
RangerMauve b0595b9
chore: move pending-invites-api into invite
c468f29
chore: denullify pending invite values
67b68a4
feat: Trust-gate more RPC methods
d3565ac
fix: Un-trust-gate redeem invite over internet
c37db5f
feat: Clear expired pending invites
7b6fe8b
feat: pending invites clear expired on init via ready-resource
3383b95
feat: Use timers to clear expired pending invites
b6ec012
chore: Remove in memory pending invites map
a93aa85
chore: Use swarmPublicKEy instead of deviceId in invite links
05d1268
chore: Fix timeout for handshake read chunk to be 10s
6a9bf28
fix: Clear member api listeners on project close
f319964
feat: Allow invite to be reused until it naturally expires
88dccaa
chore: Rename PendingInvites to InviteLinks
2b2defc
chore: refactor invite link params
987e672
chore: move pending invites api to invite links api
36b910a
feat: add expiresAt to invite links, expose in listInviteLinks
72e99a5
fix: Filter incoming invites by invite over internet invitor device ID
9553796
feat: Use HyperDHT testnet for RemoteDiscovery tests
f10b666
feat: Use testnet for invite over internet tests
38e9de2
fix: Track id from noise streams correctly.
19f9f8b
fix: Pull peer id from noise stream instead of peer remotePublicKey, …
8b413a8
feat: Encode more data in invite links, z32 for buffers instead of hex
e873ecd
feat: use crockford base32 for invite links
a1a9156
feat: cancel and deny invite over internet
2ecbae1
feat: Wire AbortSignal to RemoteDiscovery peer connection
eb7940b
fix: RemoteDiscovery Ensure connection is destroyed on disconnect
c36e919
feat: new leaveOnFail flag in Invite protobuf
2addca5
feat: new leaveOnFail flag in Invite protobuf
7a41460
feat: Leave project on fail, more robust initial sync timeout
0c829a9
test: InviteOverInternet, fail to sync and try again
6a06098
chore: use a.comapeo.app for invite over internet URL
3f919fa
feat: add seedTime to invite links, use oldest seed time or current t…
548d285
chore: rename pending-invites test to invite-links-api
f709900
docs: Guide for invite over internet
163d58c
fix: Wait for writes to finish before disconnecting from peer
8c3a752
chore: remove stray console logs
5020d7b
fix: Wait for sending ack send before emitting RPC event
668a6d2
feat: Emit invite-link-join-request without needing a project loaded
7ed2d2c
docs: Update invite over internet docs
2228e5d
feat: Distinguish reason between invitor deny and auto deny
3e5692d
test: Check that invite links seedTime works as expected
6f230a5
feat: invite link connected and accepted events on manager.
3b43a27
feat: allow list rpc methods on untrusted local peers
RangerMauve 898cbc7
feat: gate sent rpc on trust, send device info on trusted peer
RangerMauve 2c8bd3e
test: check for rejected RPC on untrusted peers
RangerMauve 9c8177e
Merge branch 'main' into feat/invite-over-internet
RangerMauve 570743b
fix: account for race conditions in finding existing peers in remote …
RangerMauve fd60941
fix: ensure remote connection ends on handshake error
RangerMauve 08eeca3
feat: Disconnect peers if they don't get trusted within 10s
RangerMauve 6514862
chore: remove only from untrusted timeout test
RangerMauve d10bb7b
feat: tighten restrictions on Ack rpc from untrusted peers
RangerMauve 6f344ca
feat: Use StartStopStateMachine for RemoteDiscovery
RangerMauve eafa908
chore: Delete old keyBy util
RangerMauve 4b699ce
feat: use 16bit length prefix and check for size being too large
RangerMauve 15b0c5f
wip: InviteLinkJoiner API for refactoring joinProjectFromURL
RangerMauve d71cea0
test: fix tests for invite joiner
RangerMauve 3bccac6
feat: replace old invite link API with new one
RangerMauve bffd48d
chore: fixes and cleanup for invite links api
RangerMauve 8ba1699
fix: remote discovery should detect keypair rotation on swarm resume,…
RangerMauve 104c72e
fix: leave peer on remote discovery connect peer timeout
RangerMauve 0cd379a
chore: More robust allow list for sending RPC
RangerMauve 4eafa5c
fix: Handle peer redeeming multiple invites over internet and disconn…
RangerMauve e4fa9c2
fix: simplify and stabilize member api
RangerMauve bc4747a
Merge branch 'main' into feat/invite-over-internet
RangerMauve b395f4d
feat: Use AuthedNoiseStream for both local and remote discovery
RangerMauve f27a6f4
feat: Allow setShouldListenOverInternet to be called more than once i…
RangerMauve 60f76c0
fix: handle connections emitting finish instead of close, avoid infin…
RangerMauve File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Some comments aren't visible on the classic Files Changed page.
There are no files selected for viewing
Large diffs are not rendered by default.
Oops, something went wrong.
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
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
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
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,113 @@ | ||
| import { TypedEmitter } from 'tiny-typed-emitter' | ||
| import { Logger } from '../logger.js' | ||
| import Hyperswarm from 'hyperswarm' | ||
| import { pEvent } from 'p-event' | ||
|
|
||
| /** @import {OpenedNoiseStream} from '../lib/noise-secret-stream-helpers.js' */ | ||
|
|
||
| /** | ||
| * @typedef {Object} DiscoveryEvents | ||
| * @property {(connection: OpenedNoiseStream) => void} connection | ||
| */ | ||
|
|
||
| /** | ||
| * @extends {TypedEmitter<DiscoveryEvents>} | ||
| */ | ||
| export class RemoteDiscovery extends TypedEmitter { | ||
| #l | ||
| /** @type {Hyperswarm?} */ | ||
| #swarm = null | ||
| /** @type {Promise<Hyperswarm>?} */ | ||
| #loading = null | ||
| #identityKeypair | ||
|
|
||
| /** | ||
| * @param {Object} opts | ||
| * @param {import('./local-discovery.js').Keypair} opts.identityKeypair | ||
| * @param {Logger} [opts.logger] | ||
| */ | ||
| constructor({ identityKeypair, logger }) { | ||
| super() | ||
| this.#l = Logger.create('RemoteDiscovery', logger) | ||
| this.#identityKeypair = identityKeypair | ||
| } | ||
|
|
||
| async #initSwarm() { | ||
| this.#l.log('Initializing swarm') | ||
|
|
||
| const swarm = new Hyperswarm({ | ||
| keyPair: this.#identityKeypair, | ||
| maxPeers: 4, | ||
| }) | ||
| swarm.on('connection', this.#handleHyperswarmConnection.bind(this)) | ||
| this.#l.log('Starting listen') | ||
| await swarm.listen() | ||
| this.#l.log('Listening') | ||
| return swarm | ||
| } | ||
|
|
||
| /** | ||
| * @returns {Promise<Hyperswarm>} | ||
| */ | ||
| async #ensureSwarm() { | ||
| await this.#loading | ||
|
RangerMauve marked this conversation as resolved.
Outdated
|
||
| if (!this.#swarm) { | ||
| this.#loading = this.#initSwarm() | ||
| this.#swarm = await this.#loading | ||
| } | ||
| return this.#swarm | ||
| } | ||
|
|
||
| /** | ||
| * Start listening for incoming connections | ||
| */ | ||
| async start() { | ||
| // TODO: Use start stop state machine | ||
|
RangerMauve marked this conversation as resolved.
Outdated
|
||
| const swarm = await this.#ensureSwarm() | ||
| await swarm.resume() | ||
| } | ||
| /** | ||
| * Close all connections and stop listening | ||
| */ | ||
| async stop() { | ||
| await this.#loading | ||
| await this.#swarm?.suspend() | ||
| } | ||
|
|
||
| async close() { | ||
| await this.#loading | ||
| await this.#swarm?.destroy() | ||
| this.#l.log('Closed swarm') | ||
| } | ||
|
|
||
| /** | ||
| * Connect to another peer by their NOISE public key | ||
| * @param {string} publicKey | ||
| * @param {object} [opts] | ||
| * @param {number} [opts.timeout] | ||
| */ | ||
| async connectPeer(publicKey, { timeout = 60_000 } = {}) { | ||
| const swarm = await this.#ensureSwarm() | ||
| const noisePublicKey = Buffer.from(publicKey, 'hex') | ||
|
|
||
| const onConnected = pEvent(this, 'connection', { | ||
|
RangerMauve marked this conversation as resolved.
|
||
| filter: (connection) => connection.remotePublicKey.equals(noisePublicKey), | ||
| timeout, | ||
| }) | ||
| // Start trying to connect | ||
| swarm.joinPeer(noisePublicKey) | ||
| this.#l.log('Connecting to %S', publicKey) | ||
|
|
||
| const socket = await onConnected | ||
|
|
||
| return socket | ||
| } | ||
|
|
||
| /** | ||
| * @param {OpenedNoiseStream} socket | ||
| * @param {import('hyperswarm').PeerInfo} _peerInfo | ||
| */ | ||
| #handleHyperswarmConnection(socket, _peerInfo) { | ||
| this.emit('connection', socket) | ||
| } | ||
| } | ||
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
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
Oops, something went wrong.
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.
Uh oh!
There was an error while loading. Please reload this page.