├── .github └── workflows │ └── workflow.yml ├── .web ├── .gitignore ├── LICENSE ├── README.md ├── docs │ ├── .vitepress │ │ ├── config.ts │ │ └── theme │ │ │ ├── components │ │ │ ├── Globe.vue │ │ │ ├── Graph.vue │ │ │ ├── HeroAnimation.vue │ │ │ ├── HomeHeroImage.vue │ │ │ ├── Landing.vue │ │ │ ├── Layout.vue │ │ │ ├── LogoCloud.vue │ │ │ ├── MeetTeam.vue │ │ │ ├── SvgImage.vue │ │ │ ├── Testimonials.vue │ │ │ ├── VPImage.vue │ │ │ ├── plans │ │ │ │ ├── PlansLanding.vue │ │ │ │ ├── README.md │ │ │ │ ├── countdown.vue │ │ │ │ ├── faq_two_columns.vue │ │ │ │ ├── settings.ts │ │ │ │ ├── two_tiers_with_emphasized_tier.vue │ │ │ │ └── with_comparison_table_on_dark.vue │ │ │ ├── positions │ │ │ │ ├── JoinUs.vue │ │ │ │ ├── PositionItem.vue │ │ │ │ ├── ThreeColumns.vue │ │ │ │ └── types.ts │ │ │ └── posts │ │ │ │ ├── Article.vue │ │ │ │ ├── Author.vue │ │ │ │ ├── Date.vue │ │ │ │ ├── Home.vue │ │ │ │ ├── Layout.vue │ │ │ │ ├── NotFound.vue │ │ │ │ ├── genFeed.ts │ │ │ │ └── posts.data.ts │ │ │ ├── index.ts │ │ │ ├── styles │ │ │ └── vars.css │ │ │ └── tailwind.postcss │ ├── blog │ │ ├── gate-api.md │ │ ├── gate-lite.md │ │ └── index.md │ ├── guide │ │ ├── adoption-plan.md │ │ ├── advertising.md │ │ ├── api │ │ │ ├── authentication.md │ │ │ ├── clients.md │ │ │ ├── examples.md │ │ │ ├── goexample │ │ │ │ ├── example_test.go │ │ │ │ ├── go.mod │ │ │ │ └── go.sum │ │ │ ├── index.md │ │ │ ├── javaexample │ │ │ │ └── SamplePlugin.java │ │ │ └── super-endpoints.md │ │ ├── auth-api.md │ │ ├── changelog.md │ │ ├── cname.png │ │ ├── connectors │ │ │ ├── diagram.svg │ │ │ ├── gate.md │ │ │ ├── index.md │ │ │ └── plugin.md │ │ ├── domains.md │ │ ├── downloads.md │ │ ├── games │ │ │ └── index.md │ │ ├── includes │ │ │ ├── downloads.md │ │ │ └── joining.md │ │ ├── index.md │ │ ├── joining.md │ │ ├── localhost.md │ │ ├── locations.md │ │ ├── offline-mode.md │ │ ├── protections.md │ │ ├── quick-start.md │ │ ├── roadmap.md │ │ ├── srv.png │ │ ├── tunnels.md │ │ ├── use-cases.md │ │ └── why.md │ ├── images │ │ ├── browser-hub.png │ │ ├── bufbuild-assets.png │ │ ├── dark-project-app-screenshot.png │ │ ├── lit.svg │ │ └── terminal-log.png │ ├── index.md │ ├── plans.md │ ├── public │ │ ├── _headers │ │ ├── blog │ │ │ ├── gate-api │ │ │ │ └── preview.png │ │ │ ├── gate-lite │ │ │ │ ├── antiddos-comparison.png │ │ │ │ ├── discord-resource-usage.png │ │ │ │ ├── discord-robin.png │ │ │ │ ├── litemode-mermaid.svg │ │ │ │ └── preview.jpeg │ │ │ └── looking-for-people.png │ │ ├── favicon.png │ │ ├── gate.png │ │ ├── img.png │ │ ├── logo.svg │ │ ├── logo_transparent-purple.png │ │ ├── logo_transparent.png │ │ ├── minekube-logo.png │ │ └── og-image.png │ ├── shared │ │ ├── cloudflare.ts │ │ └── index.ts │ └── team.md ├── package.json ├── tailwind.config.js └── yarn.lock ├── LICENSE ├── Makefile ├── README.md ├── aliases.go ├── connect.go ├── go.mod ├── go.sum ├── internal ├── ctxkey │ └── keys.go ├── ctxutil │ └── util.go ├── netutil │ └── util.go ├── testutil │ └── suite.go ├── util │ └── util.go └── wspb │ └── wspb.go ├── renovate.json ├── ws ├── client.go ├── dial.go ├── server.go └── suite_test.go └── yarn.lock /.github/workflows/workflow.yml: -------------------------------------------------------------------------------- 1 | name: workflow 2 | on: 3 | push: 4 | pull_request: 5 | types: [ opened, reopened ] 6 | permissions: 7 | contents: read 8 | # Optional: allow read access to pull request. Use with `only-new-issues` option. 9 | # pull-requests: read 10 | 11 | jobs: 12 | lint: 13 | name: lint 14 | runs-on: ubuntu-latest 15 | steps: 16 | - name: Checkout 17 | uses: actions/checkout@v3 18 | 19 | - name: Setup Go with cache 20 | uses: actions/setup-go@v4 21 | with: 22 | go-version-file: go.mod 23 | 24 | - name: golangci-lint 25 | uses: golangci/golangci-lint-action@v3 26 | with: 27 | version: latest 28 | args: --timeout 3m0s 29 | 30 | test: 31 | runs-on: ubuntu-latest 32 | steps: 33 | - name: Checkout 34 | uses: actions/checkout@v3 35 | 36 | - name: Setup Go with cache 37 | uses: actions/setup-go@v4 38 | with: 39 | cache: true 40 | go-version-file: go.mod 41 | 42 | - name: Test 43 | run: make test 44 | -------------------------------------------------------------------------------- /.web/.gitignore: -------------------------------------------------------------------------------- 1 | /docs/.vitepress/dist/ 2 | /node_modules/ 3 | .yarn 4 | .pnp.* 5 | */.vitepress/cache/ -------------------------------------------------------------------------------- /.web/README.md: -------------------------------------------------------------------------------- 1 | # Documentation Website 2 | 3 | This website is built using [Vitepress](https://vitepress.vuejs.org/), 4 | a modern static website generator for documentation. 5 | 6 | ## Setup 7 | 8 | > You must have a recent version of Node.js (14+) installed. 9 | > You may use [Volta](https://github.com/volta-cli/volta), a Node version manager, 10 | > to install the latest version of Node and `yarn`. 11 | 12 | ```sh 13 | $ curl https://get.volta.sh | bash 14 | $ volta install node yarn 15 | ``` 16 | 17 | ### Installation 18 | 19 | Finally, you will need to install the Node.js dependencies for this project 20 | using yarn or another package manager: 21 | 22 | ```sh 23 | $ yarn install 24 | ``` 25 | 26 | ### Local Development 27 | 28 | ```sh 29 | $ yarn run dev/connect 30 | ``` 31 | 32 | This command starts a local development server and opens up a browser window. 33 | Most changes are reflected live without having to restart the server. 34 | 35 | ### Build 36 | 37 | ```sh 38 | $ yarn run build/connect 39 | ``` 40 | 41 | This command generates static content into the `dist` directory and can be served 42 | using any static contents hosting service. 43 | 44 | ### Deployment 45 | 46 | Our docs are deployed using [Cloudflare Pages](https://pages.cloudflare.com). 47 | Every commit pushed to `main` branch will automatically deploy to 48 | [connect.minekube.com](https://connect.minekube.com), 49 | and any pull requests opened will have a corresponding staging URL available in 50 | the pull request comments. 51 | -------------------------------------------------------------------------------- /.web/docs/.vitepress/config.ts: -------------------------------------------------------------------------------- 1 | import {defineConfig} from 'vitepress' 2 | 3 | import {discordLink, editLink, gitHubLink, projects} from '../shared' 4 | import {additionalTitle} from "../shared/cloudflare"; 5 | import {genFeed} from "./theme/components/posts/genFeed"; 6 | 7 | export const ogUrl = 'https://connect.minekube.com' 8 | const ogImage = `${ogUrl}/og-image.png` 9 | const ogTitle = 'Minekube Connect' 10 | const ogDescription = 'The Ingress Tunnel for Minecraft Servers' 11 | 12 | export default defineConfig({ 13 | title: `Minekube Connect${additionalTitle}`, 14 | description: ogDescription, 15 | appearance: 'dark', 16 | 17 | sitemap: { 18 | hostname: ogUrl, 19 | }, 20 | 21 | head: [ 22 | ['link', {rel: 'icon', type: 'image/png', href: '/favicon.png'}], 23 | ['meta', {property: 'og:type', content: 'website'}], 24 | ['meta', {property: 'og:title', content: ogTitle}], 25 | ['meta', {property: 'og:image', content: ogImage}], 26 | ['meta', {property: 'og:url', content: ogUrl}], 27 | ['meta', {property: 'og:description', content: ogDescription}], 28 | ['meta', {name: 'theme-color', content: '#646cff'}], 29 | // [ 30 | // 'script', 31 | // { 32 | // src: 'https://cdn.usefathom.com/script.js', 33 | // 'data-site': 'CBDFBSLI', 34 | // 'data-spa': 'auto', 35 | // defer: '' 36 | // } 37 | // ] 38 | [ 39 | 'script', 40 | {}, 41 | `!function(t,e){var o,n,p,r;e.__SV||(window.posthog=e,e._i=[],e.init=function(i,s,a){function g(t,e){var o=e.split(".");2==o.length&&(t=t[o[0]],e=o[1]),t[e]=function(){t.push([e].concat(Array.prototype.slice.call(arguments,0)))}}(p=t.createElement("script")).type="text/javascript",p.async=!0,p.src=s.api_host+"/static/array.js",(r=t.getElementsByTagName("script")[0]).parentNode.insertBefore(p,r);var u=e;for(void 0!==a?u=e[a]=[]:a="posthog",u.people=u.people||[],u.toString=function(t){var e="posthog";return"posthog"!==a&&(e+="."+a),t||(e+=" (stub)"),e},u.people.toString=function(){return u.toString(1)+".people (stub)"},o="capture identify alias people.set people.set_once set_config register register_once unregister opt_out_capturing has_opted_out_capturing opt_in_capturing reset isFeatureEnabled onFeatureFlags getFeatureFlag getFeatureFlagPayload reloadFeatureFlags group updateEarlyAccessFeatureEnrollment getEarlyAccessFeatures getActiveMatchingSurveys getSurveys onSessionId".split(" "),n=0;n' 64 | }, 65 | link: 'https://app.minekube.com', 66 | ariaLabel: 'Dashboard' 67 | }, 68 | ], 69 | 70 | search: { 71 | provider: 'algolia', 72 | options: { 73 | appId: 'HW90LFDYFK', 74 | apiKey: 'ab80354b5109dc434cd770cb3db6cb2d', 75 | indexName: 'connect-minekube', 76 | }, 77 | }, 78 | 79 | // carbonAds: { 80 | // code: 'CEBIEK3N', 81 | // placement: 'vitejsdev' 82 | // }, 83 | 84 | footer: { 85 | message: `Not affiliated with Mojang nor Minecraft`, 86 | copyright: `Copyright © ${new Date().getFullYear()} Minekube and Contributors` 87 | }, 88 | 89 | nav: [ 90 | {text: 'Quick Start', link: '/guide/quick-start'}, 91 | {text: 'Downloads', link: '/guide/connectors/plugin#downloading-the-connect-plugin', activeMatch: '^/guide/connectors/plugin'}, 92 | {text: 'Connectors', link: '/guide/connectors/', activeMatch: '^/guide/connectors/'}, 93 | {text: 'Plans', link: '/plans'}, 94 | {text: 'Blog', link: '/blog/', activeMatch: '^/blog/'}, 95 | ...projects, 96 | ], 97 | 98 | sidebar: { 99 | '/guide/': [ 100 | { 101 | text: 'Getting Started', 102 | items: [ 103 | {text: 'Introduction', link: '/guide/'}, 104 | {text: 'Quick Start', link: '/guide/quick-start'}, 105 | ] 106 | }, 107 | { 108 | text: 'Connectors', 109 | items: [ 110 | {text: 'Overview', link: '/guide/connectors/'}, 111 | {text: 'Gate Connector', link: '/guide/connectors/gate'}, 112 | {text: 'Plugin Connector', link: '/guide/connectors/plugin'}, 113 | ] 114 | }, 115 | {text: 'AuthSession API', link: '/guide/auth-api'}, 116 | { 117 | text: 'Guide', 118 | items: [ 119 | { 120 | text: 'Joining Servers', 121 | link: '/guide/joining' 122 | }, 123 | { 124 | text: 'Public Localhost', 125 | link: '/guide/localhost' 126 | }, 127 | { 128 | text: 'Custom Domains', 129 | link: '/guide/domains' 130 | }, 131 | { 132 | text: 'Offline Mode', 133 | link: '/guide/offline-mode' 134 | }, 135 | { 136 | text: 'About Tunnels', 137 | link: '/guide/tunnels' 138 | }, 139 | { 140 | text: 'DDoS & Bot Protection', 141 | link: '/guide/protections' 142 | }, 143 | { 144 | text: 'Advertising Servers', 145 | link: '/guide/advertising' 146 | }, 147 | { 148 | text: 'Edge Locations', 149 | link: '/guide/locations' 150 | } 151 | ] 152 | }, 153 | { 154 | text: 'Developers API', 155 | items: [ 156 | { 157 | text: 'Overview', 158 | link: '/guide/api/' 159 | }, 160 | { 161 | text: 'API Clients', 162 | link: '/guide/api/clients' 163 | }, 164 | { 165 | text: 'Authentication', 166 | link: '/guide/api/authentication' 167 | }, 168 | { 169 | text: 'Super Endpoints', 170 | link: '/guide/api/super-endpoints' 171 | }, 172 | { 173 | text: 'Code Examples', 174 | link: '/guide/api/examples' 175 | } 176 | ] 177 | }, 178 | { 179 | text: 'Roadmap', 180 | items: [ 181 | { 182 | text: 'Changelog', 183 | link: '/guide/changelog' 184 | }, 185 | { 186 | text: 'Adoption Plan', 187 | link: '/guide/adoption-plan' 188 | }, 189 | { 190 | text: 'Use cases', 191 | link: '/guide/use-cases' 192 | }, 193 | { 194 | text: 'Development Roadmap', 195 | link: '/guide/roadmap' 196 | }, 197 | {text: 'Why', link: '/guide/why'}, 198 | ] 199 | }, 200 | // { 201 | // text: 'APIs', 202 | // items: [ 203 | // { 204 | // text: 'Plugin API', 205 | // link: '/guide/api-plugin' 206 | // }, 207 | // ] 208 | // } 209 | ], 210 | } 211 | } 212 | }) 213 | -------------------------------------------------------------------------------- /.web/docs/.vitepress/theme/components/Graph.vue: -------------------------------------------------------------------------------- 1 | 33 | 34 | 57 | 58 | -------------------------------------------------------------------------------- /.web/docs/.vitepress/theme/components/HeroAnimation.vue: -------------------------------------------------------------------------------- 1 | 12 | 13 | 50 | -------------------------------------------------------------------------------- /.web/docs/.vitepress/theme/components/HomeHeroImage.vue: -------------------------------------------------------------------------------- 1 | 32 | 33 | 44 | 45 | -------------------------------------------------------------------------------- /.web/docs/.vitepress/theme/components/Layout.vue: -------------------------------------------------------------------------------- 1 | 21 | 22 | 33 | -------------------------------------------------------------------------------- /.web/docs/.vitepress/theme/components/LogoCloud.vue: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /.web/docs/.vitepress/theme/components/MeetTeam.vue: -------------------------------------------------------------------------------- 1 | 72 | 73 | 84 | -------------------------------------------------------------------------------- /.web/docs/.vitepress/theme/components/SvgImage.vue: -------------------------------------------------------------------------------- 1 | 4 | 5 | 8 | 9 | 23 | -------------------------------------------------------------------------------- /.web/docs/.vitepress/theme/components/Testimonials.vue: -------------------------------------------------------------------------------- 1 | 80 | 81 | -------------------------------------------------------------------------------- /.web/docs/.vitepress/theme/components/VPImage.vue: -------------------------------------------------------------------------------- 1 | 12 | 13 | 38 | 39 | 48 | -------------------------------------------------------------------------------- /.web/docs/.vitepress/theme/components/plans/PlansLanding.vue: -------------------------------------------------------------------------------- 1 | 6 | 7 | 14 | -------------------------------------------------------------------------------- /.web/docs/.vitepress/theme/components/plans/README.md: -------------------------------------------------------------------------------- 1 | // please design a nice saas plans landing page 2 | // with 1 free plan and 1 paid plan called "Minekube Plus" 3 | // the page has a prominent header "Plan Pricing" and a subheader, following the plans. 4 | // below the plans is a section explaining our pricing philosophy, and a call to action to contact us for custom plans. 5 | // use tailwind classes 6 | 7 | copies: 8 | 9 | subheader: 10 | Use Minekube for free with your whole team. 11 | Upgrade to enable Browser opt-outs, and additional features. 12 | 13 | free plan: title: Free, subheader: Free for everyone 14 | plus plan: title: Minekube Plus, -------------------------------------------------------------------------------- /.web/docs/.vitepress/theme/components/plans/countdown.vue: -------------------------------------------------------------------------------- 1 | 9 | 10 | -------------------------------------------------------------------------------- /.web/docs/.vitepress/theme/components/plans/faq_two_columns.vue: -------------------------------------------------------------------------------- 1 | 19 | 20 | -------------------------------------------------------------------------------- /.web/docs/.vitepress/theme/components/plans/settings.ts: -------------------------------------------------------------------------------- 1 | export const plans = { 2 | free: { 3 | price: '$0', 4 | }, 5 | plus: { 6 | price: '$10', 7 | href: 'https://app.minekube.com/:org/checkout?plan=plus', 8 | ctaText: 'Upgrade to Plus' 9 | } 10 | } 11 | 12 | const endTime = "2024-03-31T23:59:59" 13 | export const discount = { 14 | active: new Date() < new Date(endTime), 15 | endTime: endTime, 16 | price: '$5', 17 | note: 'This is our Plus plan launch discount!' 18 | } 19 | -------------------------------------------------------------------------------- /.web/docs/.vitepress/theme/components/plans/two_tiers_with_emphasized_tier.vue: -------------------------------------------------------------------------------- 1 | 38 | 39 | -------------------------------------------------------------------------------- /.web/docs/.vitepress/theme/components/positions/JoinUs.vue: -------------------------------------------------------------------------------- 1 | 26 | 27 | 126 | -------------------------------------------------------------------------------- /.web/docs/.vitepress/theme/components/positions/PositionItem.vue: -------------------------------------------------------------------------------- 1 | 9 | 10 | 35 | -------------------------------------------------------------------------------- /.web/docs/.vitepress/theme/components/positions/ThreeColumns.vue: -------------------------------------------------------------------------------- 1 | 20 | 21 | 30 | -------------------------------------------------------------------------------- /.web/docs/.vitepress/theme/components/positions/types.ts: -------------------------------------------------------------------------------- 1 | export type Position = { 2 | role: string; 3 | description: string; 4 | salary?: string; 5 | location: string; 6 | href?: string; 7 | } -------------------------------------------------------------------------------- /.web/docs/.vitepress/theme/components/posts/Article.vue: -------------------------------------------------------------------------------- 1 | 21 | 22 | 80 | -------------------------------------------------------------------------------- /.web/docs/.vitepress/theme/components/posts/Author.vue: -------------------------------------------------------------------------------- 1 | 7 | 8 | 42 | -------------------------------------------------------------------------------- /.web/docs/.vitepress/theme/components/posts/Date.vue: -------------------------------------------------------------------------------- 1 | 10 | 11 | 19 | -------------------------------------------------------------------------------- /.web/docs/.vitepress/theme/components/posts/Home.vue: -------------------------------------------------------------------------------- 1 | 30 | 31 | 79 | -------------------------------------------------------------------------------- /.web/docs/.vitepress/theme/components/posts/Layout.vue: -------------------------------------------------------------------------------- 1 | 10 | 11 | 59 | -------------------------------------------------------------------------------- /.web/docs/.vitepress/theme/components/posts/NotFound.vue: -------------------------------------------------------------------------------- 1 | 4 | -------------------------------------------------------------------------------- /.web/docs/.vitepress/theme/components/posts/genFeed.ts: -------------------------------------------------------------------------------- 1 | import path from 'path' 2 | import { writeFileSync } from 'fs' 3 | import { Feed } from 'feed' 4 | import { createContentLoader, type SiteConfig } from 'vitepress' 5 | import {Post} from "./posts.data"; 6 | import {ogUrl} from "../../../config"; 7 | 8 | const baseUrl = ogUrl 9 | 10 | export async function genFeed(config: SiteConfig) { 11 | const feed = new Feed({ 12 | title: 'The Minekube Blog', 13 | description: 'The official blog for the Minekube platform', 14 | id: baseUrl, 15 | link: baseUrl, 16 | language: 'en', 17 | image: `${baseUrl}/minekube-logo.png`, 18 | favicon: `${baseUrl}/favicon.png`, 19 | copyright: 20 | 'Copyright (c) 2021-present, Yuxi (Evan) You and blog contributors' 21 | }) 22 | 23 | const posts = (await createContentLoader('blog/*.md', { 24 | excerpt: true, 25 | render: true, 26 | transform(raw): Post[] { 27 | return raw.filter(({ url }) => !url.endsWith('/')) // Exclude 'index.md' 28 | } 29 | }).load()) 30 | console.log(posts) 31 | 32 | posts.sort( 33 | (a, b) => 34 | +new Date(b.frontmatter.date as string) - 35 | +new Date(a.frontmatter.date as string) 36 | ) 37 | 38 | for (const { url, excerpt, frontmatter, html } of posts) { 39 | console.log(html) 40 | feed.addItem({ 41 | title: frontmatter.title, 42 | id: `${baseUrl}${url}`, 43 | link: `${baseUrl}${url}`, 44 | description: excerpt, 45 | content: html?.replaceAll('​', ''), 46 | author: [ 47 | { 48 | name: frontmatter.author, 49 | link: frontmatter.twitter 50 | ? `https://twitter.com/${frontmatter.twitter}` 51 | : undefined 52 | } 53 | ], 54 | date: frontmatter.date 55 | }) 56 | } 57 | 58 | writeFileSync(path.join(config.outDir, 'feed.rss'), feed.rss2()) 59 | } 60 | -------------------------------------------------------------------------------- /.web/docs/.vitepress/theme/components/posts/posts.data.ts: -------------------------------------------------------------------------------- 1 | import { createContentLoader } from 'vitepress' 2 | 3 | export interface Post { 4 | title: string 5 | url: string 6 | imageUrl: string 7 | date: { 8 | time: number 9 | string: string 10 | } 11 | excerpt: string | undefined, 12 | category: string, 13 | author: { 14 | name: string, 15 | role: string, 16 | href: string 17 | imageUrl: string 18 | } 19 | } 20 | 21 | declare const data: Post[] 22 | export { data } 23 | 24 | export default createContentLoader('blog/*.md', { 25 | excerpt: true, 26 | transform(raw): Post[] { 27 | return raw 28 | .filter(({ url }) => !url.endsWith('/')) // Exclude 'index.md' 29 | .map(({ url, frontmatter, excerpt }) => ({ 30 | ...frontmatter, 31 | url, 32 | excerpt, 33 | date: formatDate(frontmatter.date), 34 | })) 35 | .sort((a, b) => b.date.time - a.date.time) 36 | } 37 | }) 38 | 39 | function formatDate(raw: string): Post['date'] { 40 | const date = new Date(raw) 41 | date.setUTCHours(12) 42 | return { 43 | time: +date, 44 | string: date.toLocaleDateString('en-US', { 45 | year: 'numeric', 46 | month: 'long', 47 | day: 'numeric' 48 | }) 49 | } 50 | } -------------------------------------------------------------------------------- /.web/docs/.vitepress/theme/index.ts: -------------------------------------------------------------------------------- 1 | import './tailwind.postcss' 2 | import DefaultTheme from 'vitepress/theme' 3 | import type {Theme} from 'vitepress' 4 | import './styles/vars.css' 5 | import VPBadge from 'vitepress/dist/client/theme-default/components/VPBadge.vue' 6 | import MeetTeam from "./components/MeetTeam.vue"; 7 | import Layout from "./components/Layout.vue"; 8 | import PlansLanding from "./components/plans/PlansLanding.vue"; 9 | import PostLayout from "./components/posts/Layout.vue"; 10 | import PostHome from "./components/posts/Home.vue"; 11 | import Globe from "./components/Globe.vue"; 12 | 13 | export default { 14 | extends: DefaultTheme, 15 | Layout: Layout, 16 | enhanceApp({ app }) { 17 | // app.component('TextAndImage', SvgImage) 18 | app.component('VPBadge', VPBadge) 19 | app.component('MeetTeam', MeetTeam) 20 | app.component('PlansLanding', PlansLanding) 21 | app.component('PostHome', PostHome) 22 | app.component('Post', PostLayout), 23 | app.component('Globe', Globe) 24 | } 25 | } satisfies Theme 26 | -------------------------------------------------------------------------------- /.web/docs/.vitepress/theme/styles/vars.css: -------------------------------------------------------------------------------- 1 | /** 2 | * Customize default theme styling by overriding CSS variables: 3 | * https://github.com/vuejs/vitepress/blob/main/src/client/theme-default/styles/vars.css 4 | */ 5 | 6 | /** 7 | * Colors 8 | * 9 | * Each colors have exact same color scale system with 3 levels of solid 10 | * colors with different brightness, and 1 soft color. 11 | * 12 | * - `XXX-1`: The most solid color used mainly for colored text. It must 13 | * satisfy the contrast ratio against when used on top of `XXX-soft`. 14 | * 15 | * - `XXX-2`: The color used mainly for hover state of the button. 16 | * 17 | * - `XXX-3`: The color for solid background, such as bg color of the button. 18 | * It must satisfy the contrast ratio with pure white (#ffffff) text on 19 | * top of it. 20 | * 21 | * - `XXX-soft`: The color used for subtle background such as custom container 22 | * or badges. It must satisfy the contrast ratio when putting `XXX-1` colors 23 | * on top of it. 24 | * 25 | * The soft color must be semi transparent alpha channel. This is crucial 26 | * because it allows adding multiple "soft" colors on top of each other 27 | * to create a accent, such as when having inline code block inside 28 | * custom containers. 29 | * 30 | * - `default`: The color used purely for subtle indication without any 31 | * special meanings attached to it such as bg color for menu hover state. 32 | * 33 | * - `brand`: Used for primary brand colors, such as link text, button with 34 | * brand theme, etc. 35 | * 36 | * - `tip`: Used to indicate useful information. The default theme uses the 37 | * brand color for this by default. 38 | * 39 | * - `warning`: Used to indicate warning to the users. Used in custom 40 | * container, badges, etc. 41 | * 42 | * - `danger`: Used to show error, or dangerous message to the users. Used 43 | * in custom container, badges, etc. 44 | * -------------------------------------------------------------------------- */ 45 | 46 | :root { 47 | --vp-c-default-1: var(--vp-c-gray-1); 48 | --vp-c-default-2: var(--vp-c-gray-2); 49 | --vp-c-default-3: var(--vp-c-gray-3); 50 | --vp-c-default-soft: var(--vp-c-gray-soft); 51 | 52 | --vp-c-brand-1: #D26B1B; 53 | --vp-c-brand-2: #F38020; 54 | --vp-c-brand-3: #F59A47; 55 | --vp-c-brand-soft: rgba(243, 128, 32, 0.14); 56 | 57 | --vp-c-tip-1: var(--vp-c-brand-1); 58 | --vp-c-tip-2: var(--vp-c-brand-2); 59 | --vp-c-tip-3: var(--vp-c-brand-3); 60 | --vp-c-tip-soft: var(--vp-c-brand-soft); 61 | 62 | --vp-c-warning-1: var(--vp-c-yellow-1); 63 | --vp-c-warning-2: var(--vp-c-yellow-2); 64 | --vp-c-warning-3: var(--vp-c-yellow-3); 65 | --vp-c-warning-soft: var(--vp-c-yellow-soft); 66 | 67 | --vp-c-danger-1: var(--vp-c-red-1); 68 | --vp-c-danger-2: var(--vp-c-red-2); 69 | --vp-c-danger-3: var(--vp-c-red-3); 70 | --vp-c-danger-soft: var(--vp-c-red-soft); 71 | } 72 | 73 | .dark { 74 | --vp-c-brand-1: #F59A47; 75 | --vp-c-brand-2: #F38020; 76 | --vp-c-brand-3: #D26B1B; 77 | } 78 | 79 | /** 80 | * Component: Button 81 | * -------------------------------------------------------------------------- */ 82 | 83 | :root { 84 | --vp-button-brand-border: transparent; 85 | --vp-button-brand-text: var(--vp-c-white); 86 | --vp-button-brand-bg: var(--vp-c-brand-3); 87 | --vp-button-brand-hover-border: transparent; 88 | --vp-button-brand-hover-text: var(--vp-c-white); 89 | --vp-button-brand-hover-bg: var(--vp-c-brand-2); 90 | --vp-button-brand-active-border: transparent; 91 | --vp-button-brand-active-text: var(--vp-c-white); 92 | --vp-button-brand-active-bg: var(--vp-c-brand-1); 93 | } 94 | 95 | /** 96 | * Component: Home 97 | * -------------------------------------------------------------------------- */ 98 | 99 | :root { 100 | --my-purple: #bd34fe; 101 | 102 | --vp-home-hero-name-color: transparent; 103 | --vp-home-hero-name-background: -webkit-linear-gradient( 104 | 120deg, 105 | var(--vp-c-brand-1) 30%, 106 | var(--my-purple) 107 | ); 108 | 109 | --vp-home-hero-image-background-image: linear-gradient( 110 | -45deg, 111 | var(--my-purple) 50%, 112 | #F59A47 50% 113 | ); 114 | --vp-home-hero-image-filter: blur(44px); 115 | } 116 | 117 | @media (min-width: 640px) { 118 | :root { 119 | --vp-home-hero-image-filter: blur(56px); 120 | } 121 | } 122 | 123 | @media (min-width: 960px) { 124 | :root { 125 | --vp-home-hero-image-filter: blur(72px); 126 | } 127 | } 128 | 129 | /** 130 | * Component: Algolia 131 | * -------------------------------------------------------------------------- */ 132 | 133 | .DocSearch { 134 | --docsearch-primary-color: var(--vp-c-brand-1) !important; 135 | } -------------------------------------------------------------------------------- /.web/docs/.vitepress/theme/tailwind.postcss: -------------------------------------------------------------------------------- 1 | @tailwind base; 2 | 3 | @tailwind components; 4 | 5 | @tailwind utilities; -------------------------------------------------------------------------------- /.web/docs/blog/gate-api.md: -------------------------------------------------------------------------------- 1 | --- 2 | layout: Post 3 | title: 'Control Your Minecraft Proxy with the New Gate API' 4 | category: Engineering 5 | date: 2024-12-12 6 | imageUrl: '/public/blog/gate-api/preview.png' 7 | author: 8 | name: Gate Team 9 | role: Engineering 10 | href: 'https://github.com/minekube' 11 | imageUrl: 'https://github.com/minekube.png' 12 | --- 13 | 14 | Building tools around your Minecraft proxy shouldn't require a PhD in plugin development. Today we're launching Gate API - a simple way to control your Gate proxy from any programming language. Want to move players between servers? Check server status? Build an admin panel? Now you can do it with a few lines of code. 15 | 16 | ## Where we started 17 | 18 | Gate has always been about making Minecraft server management easier. But when it came to building tools around it, you had two options: write a plugin or parse log files. Neither was fun. 19 | 20 | We wanted something better. Something that would let you: 21 | 22 | - Build admin panels without wrestling with plugins 23 | - Create Discord bots that actually work 24 | - Automate server management without parsing logs 25 | - Do it all in the language you love 26 | 27 | ## Enter Gate API 28 | 29 | Gate API is our answer to these problems. It's a modern HTTP/gRPC API that speaks your language (literally - we support TypeScript, Python, Rust, and more). 30 | 31 | Getting started is simple: 32 | 33 | ```yaml 34 | api: 35 | enabled: true 36 | bind: localhost:8080 37 | ``` 38 | 39 | That's it. Your Gate proxy is now API-enabled. 40 | 41 | ## What can you build? 42 | 43 | Let's look at some real examples. Here's what you can do with just a few lines of code: 44 | 45 | ### Managing Players 46 | 47 | ```typescript 48 | // List all online players 49 | const { players } = await client.listPlayers({}); 50 | console.log(`${players.length} players online`); 51 | 52 | // Move a player to another server 53 | await client.connectPlayer({ 54 | player: "Notch", 55 | server: "minigames" 56 | }); 57 | ``` 58 | 59 | ### Server Management 60 | 61 | ```typescript 62 | // List servers and their status 63 | const { servers } = await client.listServers({}); 64 | servers.forEach(server => { 65 | console.log(`${server.name}: ${server.players} players`); 66 | }); 67 | 68 | // Add a new server 69 | await client.registerServer({ 70 | name: "minigames", 71 | address: "10.0.0.2:25565" 72 | }); 73 | ``` 74 | 75 | ### Real Examples 76 | 77 | Here's a simple example that shows server status: 78 | 79 | ```typescript 80 | async function getServerStatus() { 81 | const { servers } = await client.listServers({}); 82 | return servers.map(s => `${s.name}: ${s.players} players`).join('\n'); 83 | } 84 | ``` 85 | 86 | Want to auto-balance players across servers? 87 | 88 | ```typescript 89 | async function balanceServers(from: string, to: string) { 90 | const { players } = await client.listPlayers({ 91 | servers: [from] 92 | }); 93 | 94 | // Move half the players 95 | const half = Math.floor(players.length / 2); 96 | for (let i = 0; i < half; i++) { 97 | await client.connectPlayer({ 98 | player: players[i].username, 99 | server: to 100 | }); 101 | } 102 | } 103 | ``` 104 | 105 | ### Web Apps 106 | 107 | Yes, you can even use it in the browser (with proper security, of course): 108 | 109 | ```typescript 110 | const client = createClient(GateService, createConnectTransport({ 111 | baseUrl: 'https://your-secured-api.example.com' 112 | })); 113 | 114 | function ServerList() { 115 | const [servers, setServers] = useState([]); 116 | 117 | useEffect(() => { 118 | async function update() { 119 | const { servers } = await client.listServers({}); 120 | setServers(servers); 121 | } 122 | update(); 123 | return setInterval(update, 5000); 124 | }, []); 125 | 126 | return servers.map(server => 127 |
128 | {server.name}: {server.players} players 129 |
130 | ); 131 | } 132 | ``` 133 | 134 | ⚠️ **Important**: Always secure your API before exposing it to the web! 135 | 136 | ## Running in the browser 137 | 138 | Yes, you can even call Gate API directly from web apps! The API works seamlessly in browsers: 139 | 140 | ```typescript 141 | import { createClient, createConnectTransport } from '@connectrpc/connect-web'; 142 | import { GateService } from '@buf/minekube_gate.connectrpc_es/gate/v1/gate_connect'; 143 | 144 | const client = createClient( 145 | GateService, 146 | createConnectTransport({ 147 | baseUrl: 'http://localhost:8080', 148 | }) 149 | ); 150 | ``` 151 | 152 | But remember: Gate API is powerful. It can do anything your proxy can do. Before exposing it to the web, put an auth proxy in front of it (unless you want random internet users controlling your servers). 153 | 154 | ## What's next? 155 | 156 | This is just the beginning. We're working on: 157 | 158 | - WebSocket support for real-time events 159 | - More management endpoints 160 | - Better monitoring capabilities 161 | 162 | Want to try it out? Check out our [API documentation](https://gate.minekube.com/developers/api/). We can't wait to see what you build! 163 | 164 | Got questions? Join us on [Discord](https://minekube.com/discord) - we're always around to help. 165 | -------------------------------------------------------------------------------- /.web/docs/blog/index.md: -------------------------------------------------------------------------------- 1 | --- 2 | layout: PostHome 3 | title: From the Minekube Blog 4 | subtext: Read the latest news and updates from the Minekube team. 5 | --- 6 | 7 | -------------------------------------------------------------------------------- /.web/docs/guide/adoption-plan.md: -------------------------------------------------------------------------------- 1 | # The step-by-step plan to increase adoption 2 | 3 | We plan to grow the `Connect community` in phases and increase adoption of Connect 4 | step-by-step starting with targeting the most important features and audiences. 5 | 6 | The goal is to grow the following counting factors: 7 | - Connect Plugin **installations** 8 | - Servers **linked** to Connect Network 9 | - Users playing on servers **through** Connect Network 10 | 11 | ## Step #1 - Friends & Local Host Servers 12 | 13 | Our first target audience are friends that want to play together on their local host servers 14 | and servers owners experimenting with Connect. 15 | 16 | ## Step #2 - Grow creators & players pool 17 | 18 | Our second target audiences are creators that want to grow their player base and players 19 | that want to discover new servers. 20 | 21 | Here we will focus on the Connect Browser to make it an interactive 22 | in-game server list where players can surf, join and review servers with their friends. 23 | 24 | Gamification, Referral and Friends systems will be keys to success at this phase and beyond. 25 | 26 | ## Step #3 - Developers 27 | 28 | Our third target audience are developers to allow them to extend the Connect Platform 29 | and build their own plugins and services on top of it by using public Connect APIs. 30 | 31 | Connect integrates a developer platform and marketplace for Minecraft plugins 32 | and services integrated with Connect. 33 | -------------------------------------------------------------------------------- /.web/docs/guide/advertising.md: -------------------------------------------------------------------------------- 1 | # Advertising Channels for Your Server Always Free 2 | 3 | _Discovering and advertising your server is an important part of growing your community. 4 | The [Connect Network](/guide/#the-connect-network) provides ways to advertise your servers to players._ 5 | 6 | ## Browser Hub 7 | Always Free 8 | 9 | The well-known **Connect Browser Hub** is part of the [Connect Browser](/guide/#the-connect-browser) and is 10 | one of the first places that players see when joining the [Connect Network](/guide/#the-connect-network) 11 | without an [Endpoint Domain](/guide/domains). 12 | 13 | It is a central place of the [Connect Platform](/guide/#the-connect-platform) that allows players to discover and join 14 | [Connect Endpoints](/guide/#connect-endpoints) from in-game. Players also fallback to the 15 | **Browser Hub** when they get disconnected from a server or try to join an endpoint that is currently inactive. 16 | 17 | ### Opting out of the Browser Hub Plus plan 18 | 19 | If you are on the **[Plus Plan](/plans)**, you can opt out of the **Browser Hub** in your [Dashboard](/guide/#the-connect-dashboard)'s 20 | organization settings. 21 | 22 | ![Browser Hub](/images/browser-hub.png) 23 | 24 | ```rhyme 25 | High above the clouds, the player seeks, 26 | A new server to join, with Connect at its peaks. 27 | ``` 28 | 29 | ## Chat Messages 30 | This advertising method is not yet available. 31 | 32 | You can broadcast custom chat messages to players on the [Connect Network](/guide/#the-connect-network) 33 | to let them know about your server. These messages are sent periodically 34 | to certain players to not spam them. 35 | 36 | [//]: # (todo with screenshots) 37 | 38 | ## Action Bar 39 | This advertising method is not yet available. 40 | 41 | You use the action bar to display a message to players in the [Browser Hub](#browser-hub). 42 | 43 | ## Boss Bar 44 | This advertising method is not yet available. 45 | 46 | You can use the boss bar to display a message to players in the [Browser Hub](#browser-hub). 47 | -------------------------------------------------------------------------------- /.web/docs/guide/api/authentication.md: -------------------------------------------------------------------------------- 1 | # Authentication 2 | 3 | _Clients and [Endpoints](/guide/#connect-endpoints) authenticate to Connect API 4 | with an API token and the Endpoint name so that Connect knows who is making requests 5 | and what permissions you have._ 6 | 7 | --- 8 | 9 | If you are using the Connect API through the [Java API provided by Connect Plugin](/guide/api/clients#provided-by-connect-plugin) 10 | then you do not write authentication code. 11 | 12 | ## Required Headers 13 | 14 | All requests to the Connect API require the following headers: 15 | - `Connect-Endpoint` - The name of the endpoint the token belongs to. 16 | - `Authorization: Bearer ` - The token of the endpoint you are connecting to. 17 | 18 | ## Endpoint Names 19 | 20 | Connect has the concept of globally unique **endpoint names** to identify your server even after restarts. 21 | The [Connectors](/guide/connectors/) use a token file to authenticate that you 22 | own an **endpoint name** in the [Connect Network](/guide/#the-connect-network). 23 | 24 | ::: code-group 25 | ```json [plugins/connect/token.json] 26 | {"token":"T-ozinikukmabrpyzogjjl"} 27 | ``` 28 | ::: 29 | 30 | The token and endpoint name have a direct relationship. 31 | If you lose your `token.json` file and haven't imported your endpoint to the 32 | [Connect Dashboard](https://app.minekube.com), your endpoint 33 | name is lost, and you will have to use a new name. If you have imported your endpoint 34 | you can always reset your token in the dashboard. 35 | 36 | ## Super Endpoints 37 | 38 | Checkout [Super Endpoints guide](/guide/api/super-endpoints) for authorizing other endpoints to act on your Endpoint's behalf. 39 | -------------------------------------------------------------------------------- /.web/docs/guide/api/clients.md: -------------------------------------------------------------------------------- 1 | # Connect API Clients 2 | 3 | _Connect provides code generated API clients! 4 | They save developers time and effort, 5 | while ensuring consistency and maintainability. 6 | With type safety, IDE support, and language compatibility, 7 | these clients streamline the integration process, 8 | abstract away complexities, and empower developers to 9 | focus on building robust and scalable applications._ 10 | 11 | [[TOC]] 12 | 13 | ## Provided by Connect Plugin 14 | 15 | As a plugin developer you can depend on [Connect Plugin](/guide/connectors/plugin) in your plugin's dependencies. 16 | Make sure that your `plugin.yml` has a `depend: [ connect ]` to ensure that the Connect Plugin is loaded before your 17 | plugin. 18 | 19 | The Connect Plugin provides authenticated stubs to the Connect API through the `ConnectApi` global instance. 20 | 21 | ```java [Main.java Java] 22 | com.minekube.connect.api.ConnectApi.getInstance().getClients()... 23 | ``` 24 | 25 | Simply add the `connect-java:api` dependency to your project using Gradle or Maven with the 26 | [Jitpack](https://jitpack.io/#minekube/connect-java) repository. 27 | 28 | ::: code-group 29 | 30 | ```kotlin [build.gradle.kts Gradle Kotlin] 31 | repositories { 32 | maven("https://jitpack.io") 33 | } 34 | 35 | dependencies { 36 | api("com.github.minekube.connect-java:api:latest") 37 | } 38 | ``` 39 | 40 | ```groovy [build.gradle Gradle Groovy] 41 | repositories { 42 | maven { url 'https://jitpack.io' } 43 | } 44 | 45 | dependencies { 46 | api 'com.github.minekube.connect-java:api:latest' 47 | } 48 | ``` 49 | 50 | ```xml [pom.xml Maven] 51 | 52 | 53 | 54 | jitpack.io 55 | https://jitpack.io 56 | 57 | 58 | 59 | 60 | 61 | com.github.minekube.connect-java 62 | api 63 | latest 64 | provided 65 | 66 | 67 | ``` 68 | 69 | ::: 70 | 71 | Checkout [Code Examples](/guide/api/examples) to see it in action. 72 | 73 | ## Other languages 74 | 75 | You can also use 76 | [Buf Remote Packages](https://buf.build/minekube/connect/assets/main) 77 | that provide client libraries for many programming languages for the Connect API. 78 | Make sure to include the required request header to [Authenticate](/guide/api/authentication) self-built clients with 79 | the Connect API. 80 | 81 | Supported languages: 82 | 83 | - Java/Kotlin 84 | - Golang 85 | - JavaScript/TypeScript 86 | - [read more...](https://buf.build/docs/bsr/remote-packages/overview/) 87 | 88 | For example, to add the following modules to your Go project: 89 | 90 | ```shell 91 | go get github.com/bufbuild/connect-go@latest 92 | go get buf.build/gen/go/minekube/connect/bufbuild/connect-go@latest 93 | go get buf.build/gen/go/minekube/connect/protocolbuffers/go@latest 94 | ``` 95 | 96 | Checkout [Code Examples](/guide/api/examples) for code examples in different languages. 97 | 98 | --- 99 | 100 | [![Buf Remote Packages](/images/bufbuild-assets.png)](https://buf.build/minekube/connect/assets/main) 101 | -------------------------------------------------------------------------------- /.web/docs/guide/api/examples.md: -------------------------------------------------------------------------------- 1 | # Code Examples 2 | 3 | _Code examples are a great way to learn how to use the Connect API in different languages._ 4 | 5 | See also [Other languages](/guide/api/clients#other-languages). 6 | 7 | ::: code-group 8 | 9 | ```java [Connect Plugin Java] 10 | 11 | ``` 12 | 13 | ```go [Buf Remote Package Golang] 14 | 15 | ``` 16 | 17 | ::: 18 | -------------------------------------------------------------------------------- /.web/docs/guide/api/goexample/example_test.go: -------------------------------------------------------------------------------- 1 | package goexample 2 | 3 | import ( 4 | "context" 5 | "net/http" 6 | 7 | minekube "buf.build/gen/go/minekube/connect/bufbuild/connect-go/minekube/connect/v1alpha1/connectv1alpha1connect" 8 | connectpb "buf.build/gen/go/minekube/connect/protocolbuffers/go/minekube/connect/v1alpha1" 9 | 10 | // You can read more about Buf's Connect for Go here https://connect.build/docs/go 11 | "github.com/bufbuild/connect-go" 12 | ) 13 | 14 | const ( 15 | // This is the official Connect API endpoint. 16 | baseURL = "https://connect-api.minekube.com" 17 | 18 | // These are the headers you need to set to authenticate with the Connect API. 19 | endpointHeader = "Connect-Endpoint" 20 | tokenHeader = "Authorization" 21 | ) 22 | 23 | // ExampleClient_ListEndpoints shows how to list endpoints you have access to. 24 | // It uses the default http.Client and sets the endpoint and token headers 25 | // manually. 26 | func ExampleClient_ListEndpoints() { 27 | // Set up the client. 28 | client := minekube.NewConnectServiceClient(http.DefaultClient, baseURL) 29 | 30 | // Set up a request to list endpoints you have access to. 31 | ctx := context.TODO() 32 | req := connect.NewRequest(&connectpb.ListEndpointsRequest{}) 33 | req.Header().Set(endpointHeader, "my-endpoint") 34 | req.Header().Set(tokenHeader, "Bearer "+"my-token") 35 | 36 | // Fetch all endpoints until the server returns an empty page. 37 | for { 38 | // Send the request. 39 | res, err := client.ListEndpoints(ctx, req) 40 | if err != nil { 41 | panic(err) 42 | } 43 | 44 | // Print the endpoints. 45 | for _, endpoint := range res.Msg.GetEndpoints() { 46 | // Do something with the endpoint. 47 | println(endpoint) 48 | } 49 | 50 | // Prepare the next request. 51 | req.Msg.PageToken = res.Msg.GetNextPageToken() 52 | if req.Msg.PageToken == "" { 53 | // No more pages. 54 | break 55 | } 56 | } 57 | } 58 | 59 | // ExampleClient_ListEndpoints_WithHeadersTransport shows how to connect players 60 | // to an endpoint. It uses a custom http.Client that adds the endpoint and token 61 | // headers to every request automatically. 62 | func ExampleClient_ConnectEndpoint_WithHeadersTransport() { 63 | // Set up the client. 64 | httpClient := &http.Client{Transport: &headersTransport{ 65 | headers: map[string]string{ 66 | endpointHeader: "my-endpoint", 67 | tokenHeader: "my-token", 68 | }, 69 | }} 70 | client := minekube.NewConnectServiceClient(httpClient, baseURL) 71 | 72 | // Set up a request to connect a players to an endpoint you have access to. 73 | ctx := context.TODO() 74 | req := connect.NewRequest(&connectpb.ConnectEndpointRequest{ 75 | Endpoint: "my-endpoint", 76 | Players: []string{ 77 | // example player uuids, 78 | // the players must be online and on another endpoint you have access to. 79 | "11111111-1111-1111-1111-111111111111", 80 | "22222222-2222-2222-2222-222222222222", 81 | "33333333-3333-3333-3333-333333333333", 82 | }, 83 | }) 84 | 85 | // Send the request. 86 | _, err := client.ConnectEndpoint(ctx, req) 87 | if err != nil { 88 | panic(err) 89 | } 90 | } 91 | 92 | // headersTransport is a http.RoundTripper that adds headers to requests 93 | // before sending them so that we don't have to add them to every request 94 | // manually. 95 | type headersTransport struct { 96 | headers map[string]string 97 | base http.RoundTripper 98 | } 99 | 100 | // RoundTrip implements http.RoundTripper. It adds the headers to the request. 101 | func (h *headersTransport) RoundTrip(req *http.Request) (*http.Response, error) { 102 | for k, v := range h.headers { 103 | req.Header.Add(k, v) 104 | } 105 | base := h.base 106 | if base == nil { 107 | base = http.DefaultTransport 108 | } 109 | return base.RoundTrip(req) 110 | } 111 | -------------------------------------------------------------------------------- /.web/docs/guide/api/goexample/go.mod: -------------------------------------------------------------------------------- 1 | module goexample 2 | 3 | go 1.20 4 | 5 | require ( 6 | buf.build/gen/go/minekube/connect/bufbuild/connect-go v1.7.0-20230426152538-ad9b44e4a050.1 // indirect 7 | buf.build/gen/go/minekube/connect/protocolbuffers/go v1.30.0-20230426152538-ad9b44e4a050.1 // indirect 8 | github.com/bufbuild/connect-go v1.7.0 // indirect 9 | google.golang.org/protobuf v1.30.0 // indirect 10 | ) 11 | -------------------------------------------------------------------------------- /.web/docs/guide/api/goexample/go.sum: -------------------------------------------------------------------------------- 1 | buf.build/gen/go/minekube/connect/bufbuild/connect-go v1.7.0-20230426152538-ad9b44e4a050.1 h1:4LWolQseUun8p0kZC8nN9kj7M1dQlPJjv61lkdKt/B0= 2 | buf.build/gen/go/minekube/connect/bufbuild/connect-go v1.7.0-20230426152538-ad9b44e4a050.1/go.mod h1:5x7C3tv7MLXHBkeolbWEG2wf7vwyjeaE+FgdTjmP0HM= 3 | buf.build/gen/go/minekube/connect/protocolbuffers/go v1.30.0-20230426152538-ad9b44e4a050.1 h1:bUDOTKeRKFVO0I+pYYs3W9QgbKU55tlRt1n88ZAvN2w= 4 | buf.build/gen/go/minekube/connect/protocolbuffers/go v1.30.0-20230426152538-ad9b44e4a050.1/go.mod h1:pY8nIBqtBexIBlNoBXG+3gviKiC4C9QLAIJ3FHwXUDs= 5 | github.com/bufbuild/connect-go v1.7.0 h1:MGp82v7SCza+3RhsVhV7aMikwxvI3ZfD72YiGt8FYJo= 6 | github.com/bufbuild/connect-go v1.7.0/go.mod h1:GmMJYR6orFqD0Y6ZgX8pwQ8j9baizDrIQMm1/a6LnHk= 7 | github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= 8 | github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= 9 | golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 10 | google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= 11 | google.golang.org/protobuf v1.30.0 h1:kPPoIgf3TsEvrm0PFe15JQ+570QVxYzEvvHqChK+cng= 12 | google.golang.org/protobuf v1.30.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= 13 | -------------------------------------------------------------------------------- /.web/docs/guide/api/index.md: -------------------------------------------------------------------------------- 1 | # Connect API 2 | 3 | early stage 4 | 5 | _The Connect API is a powerful tool for developers 6 | to integrate Connect into their projects and products. 7 | Interact with players and [endpoints](/guide/#connect-endpoints) on the [Connect Network](/guide/#the-connect-network) 8 | through the Connect API._ 9 | 10 | ## Getting started 11 | 12 | - [API Clients](/guide/api/clients) - Connect provides code generated API clients! 13 | - [Code Examples](/guide/api/examples) - See the Connect API in action. 14 | 15 | Connect APIs supports [multiple API protocols](https://connect.build/docs/multi-protocol): 16 | 17 | - Buf's own protocol called [`Connect`](https://connect.build/docs/introduction) 18 | - _Not to be confused with the Connect API itself, the same name is coincidence._ 19 | - [gRPC](https://grpc.io/) as it's primary communication protocol. 20 | gRPC is a high performance, open source, well-known RPC framework developed by Google. 21 | - gRPC-Web - A subset of gRPC that works in the browser. 22 | -------------------------------------------------------------------------------- /.web/docs/guide/api/javaexample/SamplePlugin.java: -------------------------------------------------------------------------------- 1 | package sample; 2 | 3 | import build.buf.gen.minekube.connect.v1alpha1.ConnectEndpointRequest; 4 | import build.buf.gen.minekube.connect.v1alpha1.ConnectEndpointResponse; 5 | import build.buf.gen.minekube.connect.v1alpha1.ConnectServiceGrpc.ConnectServiceBlockingStub; 6 | import build.buf.gen.minekube.connect.v1alpha1.ListEndpointsRequest; 7 | import build.buf.gen.minekube.connect.v1alpha1.ListEndpointsResponse; 8 | import com.minekube.connect.api.InstanceHolder; 9 | import java.util.stream.Collectors; 10 | import org.bukkit.Bukkit; 11 | import org.bukkit.plugin.java.JavaPlugin; 12 | 13 | public class SamplePlugin extends JavaPlugin { 14 | 15 | @Override 16 | public void onEnable() { 17 | ConnectServiceBlockingStub client = InstanceHolder.getClients().getConnectServiceBlockingStub(); 18 | 19 | // List all endpoints 20 | ListEndpointsRequest listReq = ListEndpointsRequest.newBuilder().build(); 21 | ListEndpointsResponse listRes = client.listEndpoints(listReq); 22 | getLogger().info( 23 | "First page of active and accessible Endpoints: " + listRes.getEndpointsList()); 24 | 25 | // Move all online players to another Endpoint 26 | ConnectEndpointRequest connectReq = ConnectEndpointRequest.newBuilder() 27 | .setEndpoint("another-endpoint") 28 | .addAllPlayers(Bukkit.getOnlinePlayers().stream() 29 | .map(p -> p.getUniqueId().toString()) 30 | .collect(Collectors.toList()) 31 | ).build(); 32 | getLogger().info("Moving players: " + connectReq.getPlayersCount()); 33 | ConnectEndpointResponse connectRes = client.connectEndpoint(connectReq); 34 | getLogger().info(connectRes.toString()); 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /.web/docs/guide/api/super-endpoints.md: -------------------------------------------------------------------------------- 1 | # Super Endpoints Public Alpha 2 | 3 | ::: info Super Endpoints is in public alpha 4 | This means the Minekube Team does not recommend running large production on alpha features. 5 | It may change completely or be removed in a future release. 6 | ::: 7 | 8 | Super Endpoints are endpoints that are authorized to control another endpoint. 9 | They are configured in the [Connect Plugin](/guide/connectors/plugin) config `plugins/connect/config.yml`. 10 | 11 | ## Authorization 12 | 13 | To allow other [Endpoints](/guide/#connect-endpoints) to control your Endpoint you can add them to your 14 | super endpoints list in your [Connect Plugin](/guide/connectors/plugin) config. 15 | 16 | Super endpoints are authorized to control this endpoint via Connect API and can e.g. disconnect players from this endpoint, 17 | send messages to players, etc. You can add as many super endpoint names as you want. 18 | 19 | ## Configuration Examples 20 | 21 | ### Authorize all 22 | 23 | Let's say you have a `hub` server, a `survival` server, and a `pvp` server. 24 | You own the config of all servers and want every server to be able to send players to each other. 25 | 26 | ::: code-group 27 | ```yaml [PvP Endpoint] 28 | # As the pvp endpoint you want to allow the hub 29 | # and survival server to send players to you. 30 | endpoint: my-pvp 31 | super-endpoints: 32 | - my-hub 33 | - my-survival 34 | ``` 35 | 36 | ```yaml [Hub Endpoint] 37 | # As the hub endpoint you want to allow the pvp 38 | # and survival server to send players to you. 39 | endpoint: my-hub 40 | super-endpoints: 41 | - my-pvp 42 | - my-survival 43 | ``` 44 | ```yaml [Survival Endpoint] 45 | # As the survival endpoint you want to allow the pvp 46 | # and hub server to send players to you. 47 | endpoint: my-survival 48 | super-endpoints: 49 | - my-pvp 50 | - my-hub 51 | ``` 52 | ::: 53 | 54 | ![](https://mermaid.ink/svg/pako:eNqFkV1PwyAUhv8KObttk7kP64hZQst655Vemd7gYCsZFEKhWrf9d3F2ThPNLkj4eJ6Tw3n3sDZcAIatY7ZG-VPVINT6Xgmk-7QNrpMdU2gjlcKj2xWdFmUS353ZCTy6KQkl-fmcvkruazyxb79q1OFl0MuCTmf0opdZPh-Pr-i2s4NOFllWzC_63WRGFqu_9c81yPdpujyQ4Gvj5LtoDz__dQWLrQ_E9yD-xSABLZxmksdh7k89gK-FFhXguOXM7SqommPkWPDmsW_WgL0LIoFgOfOCShYz0IA3TLXxVnDpjXv4SucUUgKWNc_GnJnjB1LOlmk) 55 | -------------------------------------------------------------------------------- /.web/docs/guide/auth-api.md: -------------------------------------------------------------------------------- 1 | # AuthSession API Coming soon 2 | 3 | > This feature is currently unavailable. 4 | > You can view a draft of the custom Mojang AuthSession API here: https://github.com/minekube/mojang-multiauth. 5 | > For support requests, join our Discord community. https://minekube.com/discord 6 | 7 | This guide will help you configure the Connect auth session server across different platforms and 8 | understand the managed reverse proxy by Minekube Connect. 9 | 10 | ::: info Requirements 11 | -> Requires a public IP address 12 | 13 | -> Only useful if you want to use Connect with online mode players. 14 | ::: 15 | 16 | ## How it Works 17 | 18 | The Connect's AuthSession API is an innovative adaptation of the Mojang AuthSession API. It's designed to securely 19 | authenticate online mode players from the Connect Network to your server/proxy. 20 | 21 | The API distributes Mojang Sessionserver's "hasJoined" requests from online mode servers/proxies possibly behind 22 | Minekube Connect, 23 | Minehut Network, and regular Mojang. 24 | 25 | ## Using Connect AuthSession API 26 | 27 | Follow the instructions for your software below. 28 | 29 | ### Velocity 30 | 31 | ::: code-group 32 | 33 | ```shell [Terminal] 34 | java -Dmojang.sessionserver=[MINEKUBE AUTHSESSION API COMMING SOON] -jar velocity.jar 35 | ``` 36 | 37 | ::: 38 | 39 | #### Gate 40 | 41 | TBD 42 | 43 | #### Waterfall 44 | 45 | ::: code-group 46 | 47 | ```shell [Terminal] 48 | java -Dwaterfall.auth.url="[MINEKUBE AUTHSESSION API COMMING SOON]?username=%s&serverId=%s%s" -jar waterfall.jar 49 | ``` 50 | 51 | ::: 52 | 53 | #### Paper 54 | 55 | > Note: These instructions only apply if you are running Paper standalone and **NOT** under a proxy. 56 | > If you are using Gate, Velocity, Waterfall, or Lilypad, you should have already configured this, and you can safely skip 57 | > this section. 58 | 59 | Add the following CLI flags to your start script: 60 | 61 | ```shell 62 | -Dminecraft.api.auth.host=[MINEKUBE AUTHSESSION API COMMING SOON] 63 | -Dminecraft.api.account.host=[MINEKUBE AUTHSESSION API COMMING SOON] 64 | -Dminecraft.api.services.host=[MINEKUBE AUTHSESSION API COMMING SOON] 65 | -Dminecraft.api.session.host=[MINEKUBE AUTHSESSION API COMMING SOON] 66 | ``` 67 | 68 | When you are done, your script may look something like the following: 69 | 70 | ::: code-group 71 | 72 | ```shell [Terminal] 73 | java -Dminecraft.api.auth.host=[MINEKUBE AUTHSESSION API COMMING SOON] \ 74 | -Dminecraft.api.account.host=[MINEKUBE AUTHSESSION API COMMING SOON] \ 75 | -Dminecraft.api.services.host=[MINEKUBE AUTHSESSION API COMMING SOON] \ 76 | -Dminecraft.api.session.host=[MINEKUBE AUTHSESSION API COMMING SOON] \ 77 | -jar paper.jar 78 | ``` 79 | 80 | ::: 81 | 82 | In addition, ensure that you have set `enforce-secure-profile` to `false` in 83 | your [server.properties](http://server.properties) file. 84 | 85 | #### Lilypad 86 | 87 | Set the following environment variable: 88 | 89 | `LILYPAD_MOJANG_SESSIONSERVER_URL` to `[MINEKUBE AUTHSESSION API COMMING SOON]` 90 | 91 | #### BungeeCord 92 | 93 | This proxy type is currently not supported due 94 | to [an issue in BungeeCord](https://github.com/SpigotMC/BungeeCord/pull/3201); please use Gate, Velocity or Waterfall 95 | instead. 96 | 97 | ## Compatibility 98 | 99 | Only the listed software allows changing the Mojang AuthSession API url. 100 | Bungeecord is not supported. -------------------------------------------------------------------------------- /.web/docs/guide/changelog.md: -------------------------------------------------------------------------------- 1 | # Follow Connect's `#changelog`! 2 | 3 | New features, updates and improvements to the Connect platform. 4 | Subscribe to Discord `#changelog` channel to get notified about new updates. 5 | 6 | -> https://minekube.com/discord 7 | 8 | We are constantly working on improving the platform and adding new features. 9 | If you have any suggestions or issues, feel free to post to the Discord forum! -------------------------------------------------------------------------------- /.web/docs/guide/cname.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/minekube/connect/760aea16152a12793220bf677dbc5b35848d9237/.web/docs/guide/cname.png -------------------------------------------------------------------------------- /.web/docs/guide/connectors/gate.md: -------------------------------------------------------------------------------- 1 | # Gate Proxy - Connector Guide 2 | 3 | Using Gate as a Connector for your Minecraft server or proxy. 4 | It is the best supported, most frequently updated Connector with the most capabilities. 5 | 6 | -> If you haven't already, checkout [Gate's Quick Start](https://gate.minekube.com/guide/quick-start) first, 7 | it's just a tiny binary. 8 | 9 | ## Enable Connect in Gate configuration 10 | 11 | First of all, in the Gate configuration, you: 12 | 13 | 1. enable Connect mode 14 | 2. choose your endpoint name 15 | 16 | -> See [Enabling Connect](https://gate.minekube.com/guide/connect) page for how to do it. 17 | 18 | ## Gate (standard) 19 | 20 | For an introduction to Gate, see [Gate Docs](https://gate.minekube.com/guide). 21 | 22 | As one of the most capable open source Minecraft proxy in the world, Gate has first class support for Connect with 23 | online-mode. 24 | 25 | -> To customize Gate checkout the [starter plugin template](https://github.com/minekube/gate-plugin-template) 26 | 27 | ## Gate Lite Mode 28 | 29 | For an introduction to Gate Lite mode, see [Gate Lite Docs](https://gate.minekube.com/guide/lite). 30 | 31 | ::: tip Coming Soon -> Allowing online mode in Gate Lite backend routes 32 | 33 | `Connect -> Gate Lite -> Online Mode Backend` 34 | 35 | ```yaml 36 | connect: 37 | enforcePassthrough: true 38 | ``` 39 | 40 | This feature will allow to offload Connect player authentication to your Lite backend routes, 41 | by enabling pass-through connections. 42 | 43 | ::: 44 | 45 | If your backend server behind Gate Lite is online mode and let Connect authenticate players upfront, 46 | you need to use Connect's [AuthSession API](../auth-api) 47 | to allow online mode players from Connect Network to join your backend server. 48 | 49 | ## Example Setups 50 | 51 | Minekube Connect advances the way players connect and developers architect secure Minecraft servers and networks. 52 | Let's take a look at some common example setups. 53 | 54 | ### #1 Example: Online mode Velocity 55 | 56 | `Connect -> [ Gate Lite -> [ Velocity -> Papers ] ]` 57 | 58 | > We could only use Gate -> Paper without Velocity, but we wanted to show how to use Connect with an existing proxy. ( 59 | > e.g. you need Velocity plugins) 60 | 61 | - We have Velocity in online mode running on `localhost:25577` and want to use Connect through Gate. 62 | - Because Velocity is a proxy itself, we need to use Gate Lite mode for simple reverse proxying. 63 | - We enable `lite` in Gate config and add a wildcard route `*` to forward all traffic to Velocity at `localhost:25577`. 64 | - We enable `connect` mode in Gate config and choose a name for our endpoint. 65 | - To allow online mode players from Connect Network to join our online mode Velocity server, we run Velocity with 66 | `-Dmojang.sessionserver=` to use Connect's [AuthSession API](../auth-api). 67 | - Done! We can now join to our Velocity server at `.play.minekube.net` and online mode players from Connect 68 | Network 69 | can join as well. All without using port forwarding nor the [~~Java Plugi~~n](plugin.md). 70 | 71 | > Velocity here could have been replaced by another proxy like BungeeCord/Waterfall or even Gate standard 72 | > (reduce Gate layers: try to set up Example #2 instead). 73 | 74 | ### #2 Example: Paper online or offline 75 | 76 | `Connect -> [ Gate -> Papers ]` 77 | 78 | - We have Paper in online or offline mode running on `localhost:25565` and want to use Connect through Gate. 79 | - This time we chose to not enable `lite` mode in Gate config, because we want to use Gate's full proxying capabilities 80 | like switching servers with `/server` 81 | - We enable `connect` mode in Gate config and choose a name for our endpoint. 82 | - Done! We can now join to our Paper server at `.play.minekube.net` and online mode players from Connect 83 | Network can join as well thanks to Gate's first class Connect online-mode support. All without using port forwarding 84 | nor the [~~Java Plugi~~n](plugin.md) nor the [~~AuthSession API~~](../auth-api). 85 | 86 | ## Getting Support 87 | 88 | If you have any questions or need help, simply post a support request in the Minekube Community Discord 89 | forum!. https://minekube.com/discord -------------------------------------------------------------------------------- /.web/docs/guide/connectors/index.md: -------------------------------------------------------------------------------- 1 | # Connectors Overview 2 | 3 | Learn about Connectors, their function, and available options here. 4 | 5 | ## What is a Connector? 6 | 7 | A Connector at Minekube Connect is a software component that facilitates the secure 8 | communication between your Minecraft server/proxy and the Connect Edge Network by 9 | creating secure outbound tunnels for receiving player connections. 10 | 11 | ## Available Connectors 12 | 13 | **[Gate Connector](gate.md):** Recommended 14 | 15 | - Enable `connect` mode in the configuration to use it as a Connector for your 16 | server. Gate is updated most frequently and has the most capabilities. 17 | - If you have an existing Java proxy, switch to [Gate Lite mode](gate.md#gate-lite-mode) to use it as a Connector 18 | without installing the Connect Java Plugin. 19 | 20 | -> Continue with [Gate Connector Setup Guide](gate.md) 21 | 22 | **[Java Plugin](plugin.md):** Fast set-up 23 | 24 | - The Spigot/Velocity/Bungee Connect Plugin can be installed on your Minecraft server/proxy to 25 | use it as a Connector for your endpoints. 26 | 27 | -> Continue wit [Java Plugin Setup Guide](plugin.md) 28 | 29 | ## Advantages using a Connector 30 | 31 | - You want to protect and hide your public IP address from attackers? Use a Connector. 32 | - You don't have a public IP address, like running your server on a private network or at home? Use a Connector. 33 | 34 | ### Anycast Public IP 35 | 36 | In any case, Connect gives your endpoints a shared public Anycast IP address reachable from 37 | anywhere to route your player traffic through the nearest Connect Edge region to your Minecraft server. 38 | 39 | ## How does a Connector work? 40 | 41 | To better understand how a Connector works, let's consider an example. Imagine a player named Alex from the US wants to 42 | join a Minecraft server running in Singapore. 43 | 44 | 1. **Initial Connection to Nearest Edge**: Alex's request to join the server first reaches the closest Connect Edge, 45 | located in Ashburn, for example. This step ensures Alex's connection benefits from an efficient data center network 46 | infrastructure, offering faster routing compared to a potentially slower ISP route. This optimized network path 47 | reduces latency from the outset. 48 | 49 | 2. **Session Proposal to Connector**: The Edge in Ashburn then sends a session proposal to the Connector that's 50 | associated with the target server endpoint in Singapore. 51 | 52 | 3. **Connector Connects to Local Edge**: Upon receiving the proposal, the Connector in Singapore connects to its own 53 | nearest Connect Edge in Singapore. This local Edge connection optimizes latency by ensuring server outbound 54 | connections use the most efficient network paths. 55 | 56 | 4. **Secure Tunnel Established Through Both Edges**: A secure tunnel is created from the Connector in Singapore to Alex 57 | through both the Singapore and Ashburn Edges. This tunnel ensures Alex's connection to the Minecraft server is secure 58 | and efficient, minimizing latency and enhancing the gaming experience. 59 | 60 | This process, utilizing both local and player-nearby Edge servers, ensures a high-quality connection for players 61 | globally, effectively reducing latency and securing the data path. 62 | 63 | ![https://mermaid.live/edit#pako:eNptkj1vgzAQhv-K5TksjAws_diqVkpHlqt9Sq2Gs3u2W0VR_nsNF0yqwMCHee78vHBnbbxF3emI3xnJ4KODA8M4kCpHAE7OuACU1NsRTsj360_2gAqiXB3YbaKtSHR0jzx4IjTJb_TfI_8gz-Vkg3eUhBGfpu-nrt3SQSWvCIHnvQSU8_R8hduuNCWrIsboPKnAPvgIxxVsC1mVOvXs-Rd4q0BKKto0vehWn4ILs5qsdNV5zenD56KUcnl3lARYE4hSc036Loy52eAmYtPLh_lvoHd6RB7B2fKrzxM-6PSJIw66K7cW-GvQA10KBzn5_YmM7hJn3OkcLKRlLJZFtK4EeJHRmSfo8gc8E8Vk](diagram.svg) 64 | 65 | ::: info When the Player and Connector are in the same region 66 | Note that if both the Player and Connector are in the same region, they will likely be routed to the same Edge, thus 67 | the Connector will create the Tunnel directly to the Edge the Player is connected. Only one Edge would be involved in the diagram. 68 | ::: 69 | 70 | -> Why all that? Checkout [Connect Tunnels](/guide/tunnels) explained! 71 | 72 | ## Load Balancing multiple Connectors 73 | 74 | You can run multiple Connectors for the same endpoint to load balance the player traffic between them. 75 | The Connect Edge will automatically distribute the player traffic between the available endpoint's Connectors randomly. 76 | 77 | ::: tip Regional Load Balancing 78 | 79 | Note that the Connect Edge will **currently** not take into account the accurate load or regional latency of the 80 | Connectors when 81 | distributing the player connections. If you want this behavior, let us know in 82 | our [Discord](https://minekube.com/discord)! 83 | 84 | ::: -------------------------------------------------------------------------------- /.web/docs/guide/connectors/plugin.md: -------------------------------------------------------------------------------- 1 | # Java Plugin - Connector Guide 2 | 3 | Using the Connect Java Plugin as a Connector for your Minecraft server or proxy. 4 | If you have a Minecraft Java server or proxy, this the most convenient Connector for you, 5 | but it is not as capable as the [Gate Proxy Connector](gate.md) in terms of routing features and performance. 6 | 7 | The Connect Plugin is a powerful multi-platform Minecraft plugin that tunnels 8 | your players through the global [Connect Network](#the-connect-network) to your Minecraft server/proxy. 9 | 10 | -> It supports PaperMC, BungeeCord and Velocity platforms. 11 | 12 | 13 | 14 | 15 | 16 | ## Example Setups 17 | 18 | Minekube Connect advances the way players connect and developers architect secure Minecraft servers and networks. 19 | Let's take a look at some common example setups. 20 | 21 | ### #1 Example: Velocity 22 | 23 | `Connect -> [ Velocity -> Papers ]` 24 | 25 | - We have Velocity in online mode running on `localhost:25577` and want to use Connect. 26 | - We install the Connect plugin to Velocity's `plugins` folder. 27 | - We choose a name for our endpoint in the Connect plugin config `plugins/connect/config.yml`. 28 | - We start Velocity and Connect Plugin will automatically tunnel players from the Connect Network. 29 | - Done! We can now join our Velocity server at `.play.minekube.net`. Online mode players from Connect Network 30 | can 31 | join thanks to Plugins's auth session injection mechanisms. 32 | 33 | ### #2 Example: Paper 34 | 35 | `Connect -> Paper` 36 | 37 | - We have Paper running on `localhost:25565` and want to use Connect. 38 | - We install the Connect plugin to Paper's `plugins` folder. 39 | - We choose a name for our endpoint in the Connect plugin config `plugins/connect/config.yml`. 40 | - We start Paper and Connect Plugin will automatically tunnel players from the Connect Network. 41 | - Done! We can now join our Paper server at `.play.minekube.net`. Online mode players from Connect Network can 42 | join thanks to Plugin's auth session injection mechanisms. 43 | 44 | ### #3 Example: Connect `passthrough` + AuthSession API 45 | 46 | Passthrough mode and AuthSession API is Coming soon -------------------------------------------------------------------------------- /.web/docs/guide/domains.md: -------------------------------------------------------------------------------- 1 | # Use a custom domain Always Free 2 | 3 | When you create a Connect Endpoint, it is automatically given a `play.minekube.net` sub-domain, based on the endpoint’s 4 | name. This is great for testing and private servers, but when you want to go to full production you’ll want your 5 | endpoint to appear on your own domain. 6 | That’s where the Connect custom domains comes in. 7 | Let’s set up the custom domain, first step: directing traffic to your 8 | endpoint. 9 | 10 | ## Set a CNAME record Option 1 11 | 12 | The simplest option for directing traffic to your endpoint is to create a CNAME record for your custom domain that 13 | points at your `.play.minekube.net` host. For example, if you have a custom domain called `example.com` and an 14 | endpoint called `mcserver`, then you can create a CNAME record for `example.com`‘s DNS that would look like: 15 | 16 | ![cname.png](cname.png) 17 | 18 | If you want to make a subdomain like `mc`, set the `Name` to: `mc` 19 | 20 | You’ll need to configure this with your DNS provider. The example screenshot uses Cloudflare. 21 | 22 | Now, accessing `example.com` will tell the DNS system to look up `mcserver.play.minekube.net` and return its results. 23 | 24 | ## Or, set an SRV record Option 2 25 | 26 | If you want to use an SRV record instead of a CNAME, you can do that too. Use an SRV record if 27 | you can’t use a CNAME record at the root of your domain anymore, because you have other records there (a website, for 28 | example). 29 | 30 | An SRV record for `example.com` would look like: 31 | 32 | ![srv.png](srv.png) 33 | 34 | If you want to make a subdomain like `mc`, append it like so in the `Name` field: `_minecraft._tcp.mc` 35 | 36 | ## Add your domain Do not forget! 37 | 38 | You'll need to go to the [Connect Dashboard](https://app.minekube.com) and add your domain to your endpoint. 39 | 40 | 1. Go to the endpoint you want to add a domain to 41 | 2. Click on `Custom Domains` 42 | 3. Click on `Add Domain` 43 | 4. Enter your domain 44 | 45 | ### Domain verification 46 | 47 | Your domain will be verified automatically when it detects the CNAME or SRV record set up previously. 48 | When you are done, you will be able to join your connected endpoint with your domain, within a few seconds. 49 | -------------------------------------------------------------------------------- /.web/docs/guide/downloads.md: -------------------------------------------------------------------------------- 1 | # Downloads 2 | 3 | _Downloading Connect Plugin is a breeze, 4 | Just a few clicks and you'll be pleased!_ 5 | 6 | 7 | -------------------------------------------------------------------------------- /.web/docs/guide/games/index.md: -------------------------------------------------------------------------------- 1 | # Minekube Games - Publish your Minecraft Games 2 | 3 | TBD 4 | your are a dev just want to code and experiment, 5 | let players join your game and give feedback, 6 | accelerate your development with Minekube Games. 7 | 8 | "You create the game experience, we bring the players and infrastructure. 🤝" 9 | 10 | - push docker image 11 | - self-host (link with connect) 12 | - or choose from managed cloud providers (fly.io) for you 13 | - pay only for what you use 14 | - scale up and down automacially 15 | 16 | ### Games 17 | 18 | Developers can create their own games and integrate them with APIs in the Connect Network. 19 | Players are able to browse, play and rate these game servers. Developers host their games 20 | but will be able to let Connect do the deployment and scaling for them to focus on game development only. 21 | -------------------------------------------------------------------------------- /.web/docs/guide/includes/downloads.md: -------------------------------------------------------------------------------- 1 | ## Downloading the Connect Plugin 2 | 3 | The installation steps are the same as for every other Minecraft plugin 4 | _(Download jar, put in plugins folder, start server)_. Download the Connect plugin right here! 5 | 6 | | Spigot/PaperMC Plugin | Velocity Plugin | BungeeCord Plugin | 7 | |------------------------------------------------------------------------------------------------------------|----------------------------------------------------------------------------------------------------------------|------------------------------------------------------------------------------------------------------------| 8 | | [connect-spigot.jar](https://github.com/minekube/connect-java/releases/download/latest/connect-spigot.jar) | [connect-velocity.jar](https://github.com/minekube/connect-java/releases/download/latest/connect-velocity.jar) | [connect-bungee.jar](https://github.com/minekube/connect-java/releases/download/latest/connect-bungee.jar) | 9 | 10 | Ready to experience Minekube Connect? Download the latest stable release for your platform! 11 | :point_up_2: 12 | 13 | ![Console showing public domain](/images/terminal-log.png) 14 | 15 | ## Disabling "enforce secure player profiles" Required 16 | 17 | Since Minecraft 1.19 the `enforce-secure-profile` property was introduced. 18 | Players joining through the [Connect Network](/guide/#the-connect-network) to your server won't be able to join if this 19 | setting 20 | is enabled. It is safe to disable this setting as it only affects chat messages. 21 | 22 | ::: code-group 23 | 24 | ```properties [server.properties Spigot/PaperMC] 25 | enforce-secure-profile=false 26 | 27 | # If you disable online-mode, then enforce-secure-profile has no effect 28 | online-mode=true 29 | ``` 30 | 31 | ```toml [velocity.toml Velocity] 32 | force-key-authentication = false 33 | 34 | # If you disable online-mode, then force-key-authentication has no effect 35 | online-mode = true 36 | ``` 37 | 38 | ```yaml [config.yml BungeeCord] 39 | enforce_secure_profile=false 40 | 41 | # If you disable online-mode, then enforce-secure-profile has no effect 42 | online_mode=true 43 | ``` 44 | 45 | ::: 46 | -------------------------------------------------------------------------------- /.web/docs/guide/includes/joining.md: -------------------------------------------------------------------------------- 1 | ## Joining your Server 2 | 3 | Every server has a unique configurable [Endpoint](/guide/#connect-endpoints) name that directly reflects 4 | the domain players can join the server with. 5 | If you leave this field empty, Connect will use a temporary random endpoint name 6 | for your server provided by the [Random Name Service](https://randomname.minekube.net/). 7 | 8 | You can always update that endpoint name in the config: 9 | 10 | ::: code-group 11 | ```yaml [plugins/connect/config.yml] 12 | endpoint: your-server-name 13 | ``` 14 | 15 | ```yaml [Gate config.yml] 16 | connect: 17 | name: your-server-name 18 | ``` 19 | ::: 20 | 21 | > The environment variable `CONNECT_ENDPOINT` takes precedence over the configuration file. 22 | 23 | ### Joining with free provided Public Domain 24 | 25 | After installing Connect plugin and starting your server 26 | you will see the [free public domain](/guide/domains) for your server that looks like 27 | `.play.minekube.net`. 28 | 29 | ::: code-group 30 | ```shell [Server Console] 31 | [connect] Enabling connect vX.Y.Z 32 | [connect] Enpoint name: live-beru 33 | [connect] Your public address: live-beru.play.minekube.net 34 | ``` 35 | ::: 36 | 37 | Use your domain to join your server. 38 | 39 | ![Console showing public domain](/images/terminal-log.png) 40 | 41 | 42 | Ping requests are also mirrored to the endpoint server. 43 | 44 | 45 | ### Joining from Browser Hub 46 | 47 | Players can also discover your server from the in-game 48 | [Browser Hub](/guide/advertising#browser-hub) at `minekube.net` 49 | and can join with the in-game UIs or with the `/browser join ` command. -------------------------------------------------------------------------------- /.web/docs/guide/index.md: -------------------------------------------------------------------------------- 1 | # Minekube Connect Introduction 2 | 3 | -> The Ingress Tunnel for Minecraft Servers 4 | 5 | Connect is a global gateway to a DDoS-protected Minecraft native Superproxy. 6 | A developer platform with public Anycast domains for localhost allowing admins 7 | to secure and scale servers/proxies to build durable Minecraft networks in 8 | any environment easily. 9 | 10 | _Sounds like techno marketing to you? Let's break the promises down, shall we?_ 11 | 12 | -> No time to read! Get me started! [Quick Start](quick-start) / [Dashboard](https://app.minekube.com/) 13 | 14 | ## The Connect Platform 15 | 16 | The Connect Platform consists of the following components: 17 | 18 | - [The Connect Network](#the-connect-network) - A global edge network of router proxies 19 | - [The Connectors](/guide/connectors/) - Tunnels players <-> to servers through Connect Network 20 | - [The Connect Dashboard](#the-connect-dashboard) - A web interface for organizing your Minecraft network 21 | - [The Connect API](#the-connect-api) - A public API for developers 22 | 23 | [//]: # (- [The Connect Browser](#the-connect-browser) - Server discovery for players) 24 | 25 | ## The Connect Network 26 | 27 | The Connect Network consists of highly available, scalable and self-healing edge proxies 28 | built on top of best-of-breed cloud native technologies and securely interconnects all active 29 | Minecraft server and proxy endpoints through [Connectors](/guide/connectors/) 30 | without noticeable overhead. 31 | 32 | The **Connect Network** is vital to the **Connect platform** and responsible for: 33 | 34 | - Establishing [Tunnels](/guide/tunnels) between players and your servers 35 | - Providing free public [domains](/guide/domains) to your public or [localhost](/guide/localhost) servers 36 | - [Advertising](/guide/advertising) your servers to players through multiple channels like 37 | the [Browser Hub](/guide/advertising#browser-hub) 38 | - Providing features to the public [Connect API](/guide/api/) for developers 39 | 40 | Connect can replace traditional proxies like BungeeCord or Velocity and 41 | become the largest shared Minecraft network in the world. 42 | 43 | _Core features of Connect are free and will always be free for everyone._ 44 | 45 | ## The Connect Dashboard 46 | 47 | The Connect Dashboard is a web interface for managing all 48 | resources of your Minecraft network in one place. 49 | 50 | -> https://app.minekube.com/ 51 | 52 | It allows you to manage your Endpoints, Connectors, Custom Domains, 53 | analyze statistics about your network, and much more. 54 | 55 | ## The Connect API 56 | 57 | The public Connect API allows developers to build awesome 58 | Minecraft networks and applications on top of the Connect platform. 59 | 60 | It powers the [Connect Browser](#the-connect-browser) and is used 61 | to move players between endpoints and retrieve server information. 62 | 63 | Check out the [Developers API guide](/guide/api/) to learn more! 64 | 65 | ## The Connect Browser 66 | 67 | The Connect Browser encompasses all the features that allow players to discover 68 | and join endpoints on the Connect Network. **Most notably, it's the in-game server 69 | browser that players can access with the `/browser` global command or by joining `minekube.net`.** 70 | 71 | Check out the [Advertising guide](/guide/advertising) to learn more! 72 | 73 | ## Connect Endpoints 74 | 75 | A Connect endpoint is a Minecraft server or proxy that is linked with the [Connect Network](#the-connect-network) 76 | through a [Connector](/guide/connectors/) and is identified by a globally unique human-readable name. 77 | 78 | Endpoints are also referred to as _servers_ for simplicity. 79 | 80 | Endpoints are advertised to players and can be joined 81 | by using the [Connect Browser](#the-connect-browser) or moving players with the [Connect API](#the-connect-api). 82 | Any server that can serve Minecraft clients and is linked with the Connect Network can be an Endpoint. 83 | 84 | If you do not specify the Endpoint name in your [Connect Plugin](#the-connect-plugin) configuration, 85 | the plugin will ask the [Random Name Service](https://randomname.minekube.net/). 86 | 87 | **Related Guides:** 88 | 89 | - [Joining guide](/guide/joining) - Learn how to join endpoints. 90 | - [Advertising guide](/guide/advertising) - Learn about advertising your endpoints. 91 | - [Endpoint Domains guide](/guide/domains) - Learn how to use free domains for your endpoints. 92 | - [Offline Mode guide](/guide/offline-mode) - Learn how to allow offline mode players on your server. 93 | 94 | ## Connect Tunnels 95 | 96 | As soon as your server is started [Connectors](/guide/connectors/) link with 97 | Connect networking services and players can start joining your Minecraft server even if it's running 98 | [locally on your PC](/guide/localhost). 99 | 100 | Check out [How Tunnel connections work!](/guide/tunnels) for a technical explanation. 101 | 102 | ## Connect Sessions 103 | 104 | Connect defines two specific types of sessions: 105 | 106 | - **Endpoint Sessions**: These sessions are established by your [Connector](/guide/connectors/)'s Minecraft server/proxy 107 | to the Connect edge proxy to watch for incoming player sessions. 108 | - **Player Sessions**: These sessions are established by Connect edge proxies between players and available Endpoints 109 | that have an active Connector. 110 | 111 | ## Let's Speedrun the Quick Start! 112 | 113 | You have a Minecraft server locally or somewhere else? 114 | Let's get started and link it with the **Connect Network** for the first time! 115 | Just click the `Next page` button or click [Quick Start](quick-start)! 116 | 117 | ## The Connect Plugin 118 | 119 | Moved to [Connectors / Plugin](connectors/plugin). -------------------------------------------------------------------------------- /.web/docs/guide/joining.md: -------------------------------------------------------------------------------- 1 | # How to join your Connect Server 2 | 3 | 4 | -------------------------------------------------------------------------------- /.web/docs/guide/localhost.md: -------------------------------------------------------------------------------- 1 | # Public Localhost Servers 2 | 3 | _Host Minecraft servers anywhere, even on your local PC, and make them publicly joinable._ 4 | 5 | ## Use Cases 6 | 7 | Running a localhost Minecraft server is awesome because it's free and quickly set up. 8 | 9 | - **Local Development** - Run your Minecraft server locally on your PC and test with friends. 10 | - **Local Multiplayer** - Play with friends beyond your local network without port forwarding. 11 | - **Local Hosting** - Host a Minecraft server on your local PC and make it publicly joinable. 12 | - **Local Testing** - Test your Minecraft server with players from all over the world. 13 | 14 | ## Why Connect for Public Localhost Servers? 15 | 16 | - You want to host a free Minecraft server without paying a provider. 17 | - You don't want to mess with your router settings and configure port forwarding. 18 | - Or, might not even have the admin password to your router. 19 | - You want to let players join from anywhere through the Internet. 20 | 21 | ## How does it work? 22 | 23 | The problem is only players in your local network can join. This is where Connect comes in 24 | and makes your localhost servers publicly joinable through the Internet. 25 | 26 | Yes! As soon as your server is started the Connect Plugin links with the Connect Network 27 | and players can start joining your Minecraft server even if it's running locally 28 | on your PC without configuring any port forwarding. 29 | 30 | Check out [About Tunnels](/guide/tunnels) for a technical explanation. 31 | 32 | ## Getting Started 33 | 34 | Check out the [Quick Start guide](/guide/quick-start) to learn how to get started with Connect. 35 | -------------------------------------------------------------------------------- /.web/docs/guide/locations.md: -------------------------------------------------------------------------------- 1 | # Where are our Connect Proxies located? 2 | 3 | We operate multiple Connect proxies around the world to reach users worldwide. 4 | 5 | 6 | 7 | 8 | 9 | ## Our locations 10 | 11 | - Ashburn, Virginia (US) (iad) 12 | - London, United Kingdom (lhr) 13 | - Guadalajara, Mexico (gdl) 14 | - Warsaw, Poland (waw) 15 | - Los Angeles, California (US) (lax) 16 | - Amsterdam, Netherlands (ams) 17 | - Tokyo, Japan (nrt) 18 | - Sao Paulo, Brazil (gru) 19 | - Bucharest, Romania (otp) 20 | - Hong Kong, Hong Kong (hkg) 21 | - Singapore, Singapore (sin) 22 | 23 | ## How everyone gets routed to their nearest Connect proxy 24 | 25 | We take advantage of anycast routing, which means that all Connect proxies share the same IP address. When you connect to the Connect network, your internet provider automatically routes you to the nearest Connect proxy. 26 | -------------------------------------------------------------------------------- /.web/docs/guide/offline-mode.md: -------------------------------------------------------------------------------- 1 | # Offline Mode 2 | 3 | _This page explains how to allow players to join your server in Offline Mode._ 4 | 5 | ## What is Offline Mode? 6 | 7 | Offline mode is a feature of Minecraft Vanilla that allows players to join a server without needing an Internet connection 8 | to authenticate their Minecraft account. This is useful for LAN parties or for players who don't own a Minecraft account. 9 | There are also many public offline-mode servers that allow players to join without a paid Minecraft account. 10 | 11 | Offline mode servers and unauthenticated players are often referred to as _cracked_ servers and players. 12 | 13 | ## Joining the [Connect Browser Hub](/guide/advertising#browser-hub) 14 | 15 | To join the Browser Hub as a cracked player, you can use `cracked.minekube.net` to join the [Connect Network](/guide/#the-connect-network). 16 | 17 | ## Enabling Offline Mode 18 | 19 | It is possible to join the [Connect Network](/guide/#the-connect-network) without 20 | a valid Minecraft account. By default, Connect ensures that only Mojang authenticated players can join your online mode server. 21 | 22 | To allow unauthenticated players to join your offline-mode server, you have to set the 23 | `allow-offline-mode-players` option in the [Connect Plugin](/guide/connectors/plugin) configuration 24 | to `true`. 25 | 26 | ::: code-group 27 | ```yaml [plugins/connect/config.yml] 28 | allow-offline-mode-players: true 29 | ``` 30 | ::: 31 | 32 | Offline-mode player connections are not encrypted between the player and the [Connect Network](/guide/#the-connect-network) edge. 33 | Player connections are always encrypted between the Connect edge and [Connect Endpoints](/guide/#connect-endpoints) - thanks to [Connect Tunnels](/guide/tunnels). 34 | -------------------------------------------------------------------------------- /.web/docs/guide/protections.md: -------------------------------------------------------------------------------- 1 | # Server Protections 2 | 3 | _Server protections are a set of features that protect your server from malicious players and attacks._ 4 | 5 | The best way to protect your server is to hide the public IP behind the 6 | [Connect Network](/guide/#the-connect-network) and enable online-mode. 7 | 8 | ## DDoS Protection 9 | 10 | When hiding your server behind Connect, the network tries to protect your server from DDoS attacks by 11 | using a distributed network of proxies to route player traffic through. 12 | This means that if one of the proxies in the network is under attack, 13 | other proxies will take over real player traffic and protect your server from being overloaded. 14 | 15 | ## Bot Protection 16 | This feature is a Draft and not yet available 17 | 18 | Connect uses a set of heuristics to detect bots and malicious players. 19 | If a player is detected as a bot, they will be disconnected from the network. 20 | 21 | Methods used to detect bots: 22 | - Player movement 23 | - Human verification captcha 24 | - Heuristic to check for sending too many packets per second 25 | 26 | One great method to make it harder for spambots is to enable online-mode for your server to 27 | only allow players to join your server if they have a valid Minecraft account. 28 | 29 | ::: tip 30 | 31 | Protecting your server from bots is a never ending battle. 32 | If you have a good idea on how to improve the bot protection, please let us know on our [Discord](https://minekube.com/discord). 33 | 34 | ::: 35 | -------------------------------------------------------------------------------- /.web/docs/guide/quick-start.md: -------------------------------------------------------------------------------- 1 | # Quick Start 2 | 3 | _This page explains the steps to use Connect with your Minecraft 4 | server or network._ 5 | 6 | :::: info Prerequisites 7 | 8 | - You have a Minecraft server running anywhere (locally or remote). 9 | - You have a Minecraft client for joining (paid or non-paid). 10 | :::: 11 | 12 | ## Go to the Connect Dashboard! Optional 13 | 14 | Proceed the `Quick Start` from there: 15 | 16 | -> https://app.minekube.com 17 | 18 | If you want to use the Connect Dashboard after the set-up, follow the steps below. 19 | You can later import your Minecraft Endpoints to the dashboard to manage them. 20 | 21 | ## Step 1: Choose a Connector 22 | 23 | Connectors are the link between your Minecraft Endpoints and the Connect Network. 24 | 25 | -> [Go to Available Connectors](/guide/connectors/#available-connectors) 26 | 27 | ## Step 2: Launch your Connector 28 | 29 | Follow the guide of the Connector you chose to install and launch it. 30 | The first time the Connector is launched, the Endpoint will be protected with a token, 31 | that you can reset in the Dashboard at any time. 32 | 33 | -> If you face serious issues [get help](#getting-help) in the Discord. 34 | 35 | ## Done! 36 | 37 | Your Minecraft server is now protected by the Connect Network and players can join it 38 | at `.play.minekube.net`! 39 | 40 | **Next Steps:** Consider to import your Endpoints to the Connect Dashboard. 41 | 42 | ## Getting Help 43 | 44 | If you need help, join the Minekube Community Discord 45 | and post a support request in the **#support** forum. 46 | 47 | -> https://minekube.com/discord 48 | 49 | ::: tip Endpoint Token 50 | 51 | If your get an authentication error from the Watch service, then try to reset the token 52 | in the Dashboard and update it in your `connect.json` / `token.json` file of your Connector. 53 | 54 | ::: -------------------------------------------------------------------------------- /.web/docs/guide/roadmap.md: -------------------------------------------------------------------------------- 1 | # Connect Development 2 | 3 | _This roadmap is a living document and will be updated as Connect progresses._ 4 | 5 | ## Project Roadmap 6 | 7 | We are just getting started with the open Connect platform, and we are excited to see what the future holds. 8 | There are a lot of ideas being worked on hard to extend Connect and make it even more useful for creators and players. 9 | 10 | Our major goal is to keep Connect a stable and reliable service that is 11 | always on, always developing and the core features free to use for everyone. 12 | 13 | ## Additional components for Connect Platform 14 | These are Drafts and not yet available 15 | 16 | Beyond what is already available mentioned in the [Introduction](/guide/), we have a lot of ideas to extend Connect. 17 | 18 | ### Search 19 | 20 | - Find the top-rated game servers and quickly join them 21 | - See the history of servers you were connected on to join back later 22 | - Search for any game server registered with Connect Network and filter by rating, category, tags, and more 23 | 24 | ### Reviews 25 | 26 | Players can rate and review game servers they played on a certain amount of time and get rewarded for it. 27 | This helps other players to find the best game servers when browsing server categories. 28 | 29 | ### Maps 30 | 31 | **As a player:** 32 | You walk through the world and explore previews of every game server. 33 | You like what you see? Jump into its portal, and you get connected to the server in a blink of an eye! 34 | 35 | **As a server creator:** 36 | You design the gate of your game server to make it appealing to players walking by. 37 | Only limited to the Minecraft blocks available and your imagination you have an area 38 | where you can transfer the spirit of your games to advertise your server to new players. 39 | 40 | ### Games 41 | 42 | Developers can create their own games and integrate them with APIs in the Connect Network. 43 | Players are able to browse, play and rate these game servers. Developers host their games 44 | but will be able to let Connect do the deployment and scaling for them to focus on game development only. 45 | 46 | ### Cloud 47 | 48 | The Minekube Cloud will be a hosting platform that allows players and creators to create and manage 49 | servers on-demand and pay only for the time they use it. Players are able to control their servers 50 | and resources in-game from the Connect Hub and Dashboard. 51 | 52 | ## Reminders 53 | 54 | - We would like to support [Skript](https://forums.skunity.com/resources/skript.323/) with an addon 55 | to easily integrate Connect into your Skript scripts and manage your servers and players without needing to 56 | maintain a separate proxy. 57 | 58 | --- 59 | 60 | 61 | **There are plenty of more use cases, stay tuned and join the [Discord](https://minekube.com/discord) to talk about your ideas!** 62 | -------------------------------------------------------------------------------- /.web/docs/guide/srv.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/minekube/connect/760aea16152a12793220bf677dbc5b35848d9237/.web/docs/guide/srv.png -------------------------------------------------------------------------------- /.web/docs/guide/tunnels.md: -------------------------------------------------------------------------------- 1 | # About Connect Tunnels 2 | Networking knowledge recommended 3 | 4 | _Don't believe in magic? This page explains how [Connect Tunnels](/guide/#connect-tunnels) enable player connections beyond private networks._ 5 | 6 | [[toc]] 7 | 8 | ## Introduction 9 | 10 | Publicly routable IP addresses are required to allow inbound connections from the Internet (outside your home network). 11 | Your ISP (Internet Service Provider) assigns you a public IP address, but sometimes it's dynamic and changes over time. 12 | 13 | Further, your router has a NAT (Network Address Translation) that allows multiple devices to share the same 14 | public IP address in your home network. This is why you can have multiple devices connected to the Internet 15 | at the same time, their private IP addresses are translated to your public IP address when communicating with the Internet. 16 | 17 | **However, your home firewall blocks inbound connections by default, including Minecraft connections on port `25565`.** 18 | 19 | ## Connect to the Rescue 20 | 21 | Connect bypasses this whole problem by establishing outbound only tunnel connections from your server 22 | to publicly routable edge proxies in the [Connect Network](/guide/#the-connect-network). 23 | This way, you don't have to configure port forwarding on your router. 24 | 25 | Connect edge proxies accept inbound connections from players and routes them through encrypted tunnels to your server. 26 | The [Connectors](/guide/connectors/) establish a tunnel connection to the edge proxy for every player that joins your 27 | server. 28 | 29 | Attentive readers are now wondering how the plugin knows when a player wants to join and from which edge proxy. 30 | This is where the [Connect Session Service](/guide/#connect-sessions) comes in. 31 | The plugin watches for player session proposals from this service and establishes a tunnel connection to the correct edge proxy. 32 | 33 | ## The Join Flow 34 | 35 | This is how the join flow looks for every player: 36 | 37 | 1. Connect plugin watches for player session proposals 38 | 2. A player on an edge proxy wants to join your server and sends a session proposal 39 | 3. Your server accepts the proposal and establishes a tunnel to the edge proxy to take over the player session 40 | 41 | This flow is quite fast and happens within a few milliseconds. 42 | The player doesn't notice any difference and can play on the performant tunnel to your server as usual. 43 | 44 | ## Connectors 45 | 46 | Check out the [Connectors Overview](/guide/connectors/) to learn more about Connectors. 47 | 48 | --- 49 | 50 | **If you find these concepts intimidating, don't worry! The [Quick Start](/guide/quick-start) only requires basic 51 | knowledge, and you should be able to follow along without being an expert in any of these.** 52 | 53 | -------------------------------------------------------------------------------- /.web/docs/guide/use-cases.md: -------------------------------------------------------------------------------- 1 | # Use cases 2 | 3 | _This use-cases list is a living document and will be updated as Connect progresses._ 4 | 5 | ## Make your local Minecraft server public 6 | 7 | Yes! As soon as your server is connected to the Connect Network 8 | players can join your Minecraft server even if it's running locally 9 | on your PC without configuring any port forwarding. 10 | You can read along [here](/guide/#public-localhost-servers). 11 | 12 | ## Interconnect your Minecraft servers easily 13 | 14 | The Connect Network serves as a Proxy-as-a-Service and replaces 15 | traditional Minecraft proxies like BungeeCord. You no longer need 16 | to configure and maintain a secure proxy for your Minecraft servers. 17 | The only thing you need is the [Connect Plugin](/guide/quick-start#downloading-the-connect-plugin) 18 | installed on your servers. 19 | 20 | ## Advertise your server to more players 21 | 22 | Connect is a public place to browse through all _Connected_ servers 23 | from ingame. That means players either directly join your server by your domain 24 | or discover your server in the Connect Browser Hub. 25 | 26 | ## Move players between your servers without managing an own proxy 27 | 28 | No more need to deploy your own proxies since Connect Network already 29 | works like a global shared proxy where servers and even other sub-proxies 30 | can connect to. 31 | 32 | As the server owner you can control all your connected servers and players. 33 | There will be public APIs available for you to manage your connected endpoints (servers or proxies) 34 | and players like sending players a message, moving them between your servers and so on. 35 | -------------------------------------------------------------------------------- /.web/docs/guide/why.md: -------------------------------------------------------------------------------- 1 | # Why 2 | 3 | _We recommend reading the [Introduction](index) if you haven't already._ 4 | 5 | Let's start with where we come from, the problem definition, 6 | different parties involved and the solution space. 7 | 8 | ## Table of Contents 9 | 10 | [[toc]] 11 | 12 | ## Connect is for everyone! 13 | 14 | It doesn't matter if you are a small server owner or a large network operator 15 | or even a developer who wants to integrate Connect into their projects 16 | or a player who only wants to play on servers. _Connect is for you!_ 17 | 18 | **Vision:** We connect player and creator communities world-wide. 19 | 20 | **Mission:** Our mission is to organize and connect Minecraft communities 21 | and make them universally accessible for all participants. 22 | 23 | **There are two core audiences that we want to connect and support...** 24 | 25 | ### Creators 26 | 27 | **Connect** is for Creators, they are the heart of the Minecraft community. They are 28 | the ones who make the game fun for players. They can be 29 | builders creating worlds, designers creating art, 30 | developers creating plugins, server admins hosting servers, 31 | content creators creating videos, managers creating communities, 32 | leaders creating teams and organizations, people like you reading this documentation 33 | or a creator can be all-in-one. 34 | 35 | As a creator, you want to: 36 | - Get more players to discover and play your game servers 37 | - Build a community around your game servers 38 | - Possibly make money with it 39 | 40 | ::: details Sometimes creators have to manage... 41 | 42 | infrastructure, software, plugins, larger community, 43 | finances, security, performance, updates, backups, downtime, scaling, 44 | monitoring, documentation, support, billing, marketing, advertising, 45 | analytics, legal, privacy, compliance, regulations, taxes, reputation, ... 46 | 47 | ::: 48 | 49 | ### Players 50 | 51 | _Connect_ is for Players, they are the soul of the Minecraft community. They are the ones who 52 | play the game, explore the worlds, join servers, play with friends, 53 | and make creators feel fulfilled. 54 | 55 | As a Player, you want to 56 | - Discover new Minecraft servers 57 | - Play with your friends on your favorite servers 58 | - Make friends with other players 59 | - Get rewarded for doing certain tasks 60 | 61 | ## Problems of Creators 62 | 63 | #### First Steps are the hardest 64 | 65 | No matter how good your game server is players tend to not play on empty Minecraft servers: 66 | "If there are no players, the server is irrelevant" they think. 67 | Even worse if your game concept relies on a certain amount of players to be fun and enjoyable. 68 | 69 | You could advertise on social media, forums, websites, etc. and hope that 70 | players will find it. You can also pay for advertising, but that's expensive, 71 | and you don't know if it's worth it. 72 | 73 | #### Lack of Server Discovery 74 | 75 | Servers don't get votes without players, servers don't get players without votes. 76 | Voting server-list websites are not enough to launch your amazing projects. 77 | 78 | The first players are the most difficult to convince coming back to your server. 79 | You need to have a stable daily recurring player base in order to compete for more 80 | players and votes. 81 | 82 | ## Problems of Players 83 | 84 | There is no gamification of the server discovery process. They have to search 85 | through server-list websites and other sources and copy-paste server addresses. 86 | Players don't get rewarded for playing and reviewing new servers. 87 | 88 | That's why players tend to play on the same servers over and over again 89 | and miss out on new and exciting servers. 90 | 91 | ## Solutions - brought to you by Connect 92 | 93 | ### Organic Traffic 94 | 95 | One of the most powerful features of the Connect platform is the ability to 96 | serve organic traffic to your Minecraft servers. This means that players 97 | can discover your server from the in-game server browser and join without 98 | additional effort. This is a huge advantage over traditional voting websites 99 | where players have to copy and paste server addressed into their Minecraft client. 100 | 101 | ### Both parties involved 102 | 103 | Thanks to side effects the Connect Network grows exponentially 104 | as the following two factors increase: 105 | - **Creators** are incentivized to link their server to Connect 106 | because they can passively advertise their server to get more player traffic. 107 | - **Players** are attracted to the Connect Network because it's the place where 108 | they discover increasingly more new servers and content more easily with their friends. 109 | - The loop continues... 110 | 111 | _This is the same effect as we see with the TikTok platform._ 112 | 113 | ### Equal Opportunity 114 | 115 | The traditional Minecraft server listings are outdated and have a number of [drawbacks](#lack-of-server-discovery). 116 | Connect is a fair and equal opportunity platform that favours new and smaller creators too 117 | by giving them chances to get discovered by players. 118 | 119 | ## Why Minecraft? 120 | 121 | _Read along to learn more about the Minecraft multiplayer space or continue to the [Downloads page](/guide/downloads)._ 122 | 123 | --- 124 | 125 | Today more and more games in the industry are being designed such that players can express their creativity and become content creators of the game platform itself leading the community to have access to an endless resource pool of new game content helping to reduce the effort needed of the platform maker trying to keep the player interest and engagement high. 126 | 127 | One of the largest such game platforms is Minecraft where many communities of players and creators can build and program highly customizable game experiences where only the imagination is your limit. 128 | 129 | It is no secret that Minecraft is one of the most popular games out there with millions of players every day and steadily rising. With Minecraft's $2.5 billion dollar acquisition by Microsoft in 2014 the game numbers and communities around it are blowing up even more due to younger generations loving the game. 130 | 131 | Some people of the community are no longer only players, but love creating game servers for players to join every day. Those people needed a higher skill set to configure, launch and maintain such a game server until more and more hosting companies reduced the entry barrier, the price and improved the customer experience for becoming a server owner. The number of managed Minecraft server providers grew and near to anyone can now create a server within minutes with little to no money needed. 132 | 133 | This results into a fleet of new game servers created every week. Owning a good game server is only step one, you still need to have a traffic source for new players finding out about your server in order for you as the admin to establish a recurring daily player base and grow, which is very hard. 134 | 135 | Introducing [Connect](/guide/)! 136 | -------------------------------------------------------------------------------- /.web/docs/images/browser-hub.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/minekube/connect/760aea16152a12793220bf677dbc5b35848d9237/.web/docs/images/browser-hub.png -------------------------------------------------------------------------------- /.web/docs/images/bufbuild-assets.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/minekube/connect/760aea16152a12793220bf677dbc5b35848d9237/.web/docs/images/bufbuild-assets.png -------------------------------------------------------------------------------- /.web/docs/images/dark-project-app-screenshot.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/minekube/connect/760aea16152a12793220bf677dbc5b35848d9237/.web/docs/images/dark-project-app-screenshot.png -------------------------------------------------------------------------------- /.web/docs/images/lit.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /.web/docs/images/terminal-log.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/minekube/connect/760aea16152a12793220bf677dbc5b35848d9237/.web/docs/images/terminal-log.png -------------------------------------------------------------------------------- /.web/docs/index.md: -------------------------------------------------------------------------------- 1 | --- 2 | layout: home 3 | 4 | title: Minekube Connect 5 | titleTemplate: The Ingress Tunnel for Minecraft Servers 6 | 7 | hero: 8 | name: Minekube Connect 9 | text: The Ingress Tunnel for Minecraft Servers 10 | # text: Protect Minecraft servers from attacks 11 | tagline: | 12 | A global gateway to a DDoS-protected developer platform with public domains for localhost. 13 | Connect → The Minecraft native Superproxy 14 | # tagline: Get your game servers in front of players instantly and link with the global Connect Network today! Browse servers on minekube.net 15 | image: 16 | src: /minekube-logo.png 17 | actions: 18 | - theme: brand 19 | text: Quick Setup 20 | link: /guide/quick-start 21 | - theme: brand 22 | text: Download Plugin 23 | link: /guide/connectors/plugin#downloading-the-connect-plugin 24 | - theme: alt 25 | text: Dashboard 26 | link: https://app.minekube.com 27 | - theme: alt 28 | text: Join Discord 29 | link: https://minekube.com/discord 30 | 31 | features: 32 | - icon: 📡 33 | title: Ingress for Minecraft Servers 34 | details: Protect Minecraft servers from attacks with Minekube Connect. 35 | link: /guide/quick-start 36 | linkText: Managed Ingress for Minecraft 37 | - icon: 🚇 38 | title: Connect Tunnels 39 | details: As if Cloudflare Tunnel and Minekube Gate proxy had a baby. 40 | link: /guide/tunnels 41 | linkText: About Tunnels 42 | - icon: 💻 43 | title: No more Port Forwarding 44 | details: Let your friends join localhost by NAME.play.minekube.net 45 | link: /guide/localhost 46 | linkText: Run locally, play publicly 47 | - icon: ✨️ 48 | title: Free Public Domain 49 | details: Don't have a domain? Get a free public domain for every server. 50 | link: /guide/domains 51 | linkText: Public Domains 52 | - icon: 🌐 53 | title: Shared Minecraft Network 54 | details: Securely connect to the open Connect Network to link and advertise your game servers. 55 | link: /guide/#the-connect-network 56 | linkText: The Connect Network 57 | - icon: 📦 58 | title: Quick Installation 59 | details: All you need is the Connect Plugin to link your game servers with the open Connect Network. 60 | link: /guide/quick-start 61 | linkText: Installation 62 | - icon: ⚡️ 63 | title: Advertise Your Server 64 | details: Get your server in front of players with organic traffic. 65 | link: /guide/advertising 66 | linkText: Advertising 67 | - icon: 🍀 68 | title: Equal Opportunity 69 | details: Connect is a fair and equal opportunity platform that favours new and smaller servers. 70 | link: /guide/adoption-plan 71 | linkText: Adoption Plan 72 | --- 73 | -------------------------------------------------------------------------------- /.web/docs/plans.md: -------------------------------------------------------------------------------- 1 | --- 2 | layout: page 3 | title: Plan Pricing 4 | description: Minekube Connect plans and pricing philosophy. 5 | --- 6 | 7 | 8 | -------------------------------------------------------------------------------- /.web/docs/public/_headers: -------------------------------------------------------------------------------- 1 | /assets/* 2 | cache-control: max-age=31536000 3 | cache-control: immutable 4 | 5 | /*.svg 6 | cache-control: max-age=604800 7 | cache-control: immutable 8 | 9 | /*.png 10 | cache-control: max-age=604800 11 | cache-control: immutable 12 | -------------------------------------------------------------------------------- /.web/docs/public/blog/gate-api/preview.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/minekube/connect/760aea16152a12793220bf677dbc5b35848d9237/.web/docs/public/blog/gate-api/preview.png -------------------------------------------------------------------------------- /.web/docs/public/blog/gate-lite/antiddos-comparison.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/minekube/connect/760aea16152a12793220bf677dbc5b35848d9237/.web/docs/public/blog/gate-lite/antiddos-comparison.png -------------------------------------------------------------------------------- /.web/docs/public/blog/gate-lite/discord-resource-usage.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/minekube/connect/760aea16152a12793220bf677dbc5b35848d9237/.web/docs/public/blog/gate-lite/discord-resource-usage.png -------------------------------------------------------------------------------- /.web/docs/public/blog/gate-lite/discord-robin.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/minekube/connect/760aea16152a12793220bf677dbc5b35848d9237/.web/docs/public/blog/gate-lite/discord-robin.png -------------------------------------------------------------------------------- /.web/docs/public/blog/gate-lite/litemode-mermaid.svg: -------------------------------------------------------------------------------- 1 |
Join example.com
Join my.example.com
10.0.0.1
10.0.0.2
10.0.0.3
Player Alice
Gate Lite
Player Bob
Backend A
Backend B
Another Proxy
2 | -------------------------------------------------------------------------------- /.web/docs/public/blog/gate-lite/preview.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/minekube/connect/760aea16152a12793220bf677dbc5b35848d9237/.web/docs/public/blog/gate-lite/preview.jpeg -------------------------------------------------------------------------------- /.web/docs/public/blog/looking-for-people.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/minekube/connect/760aea16152a12793220bf677dbc5b35848d9237/.web/docs/public/blog/looking-for-people.png -------------------------------------------------------------------------------- /.web/docs/public/favicon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/minekube/connect/760aea16152a12793220bf677dbc5b35848d9237/.web/docs/public/favicon.png -------------------------------------------------------------------------------- /.web/docs/public/gate.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/minekube/connect/760aea16152a12793220bf677dbc5b35848d9237/.web/docs/public/gate.png -------------------------------------------------------------------------------- /.web/docs/public/img.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/minekube/connect/760aea16152a12793220bf677dbc5b35848d9237/.web/docs/public/img.png -------------------------------------------------------------------------------- /.web/docs/public/logo.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /.web/docs/public/logo_transparent-purple.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/minekube/connect/760aea16152a12793220bf677dbc5b35848d9237/.web/docs/public/logo_transparent-purple.png -------------------------------------------------------------------------------- /.web/docs/public/logo_transparent.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/minekube/connect/760aea16152a12793220bf677dbc5b35848d9237/.web/docs/public/logo_transparent.png -------------------------------------------------------------------------------- /.web/docs/public/minekube-logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/minekube/connect/760aea16152a12793220bf677dbc5b35848d9237/.web/docs/public/minekube-logo.png -------------------------------------------------------------------------------- /.web/docs/public/og-image.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/minekube/connect/760aea16152a12793220bf677dbc5b35848d9237/.web/docs/public/og-image.png -------------------------------------------------------------------------------- /.web/docs/shared/cloudflare.ts: -------------------------------------------------------------------------------- 1 | // cloudflare envs 2 | export const commitRef = process.env.CF_PAGES_COMMIT_SHA?.slice(0, 8) || 'dev' 3 | 4 | export const deployType = (() => { 5 | if (commitRef === '') { 6 | return 'local' 7 | } 8 | return 'release' 9 | })() 10 | 11 | export const additionalTitle = ((): string => { 12 | if (deployType === 'release') { 13 | return '' 14 | } 15 | return ' (local)' 16 | })() -------------------------------------------------------------------------------- /.web/docs/shared/index.ts: -------------------------------------------------------------------------------- 1 | import {DefaultTheme} from "vitepress"; 2 | 3 | export const discordLink = 'https://minekube.com/discord' 4 | export const gitHubLink = 'https://github.com/minekube' 5 | 6 | export const projects: DefaultTheme.NavItem[] = [ 7 | { 8 | text: 'Gate Proxy', 9 | link: 'https://gate.minekube.com', 10 | }, 11 | { 12 | text: 'Dashboard', 13 | link: 'https://app.minekube.com', 14 | }, 15 | ] 16 | 17 | export const editLink = (project: string): DefaultTheme.EditLink => { 18 | return { 19 | pattern: `${gitHubLink}/${project}/edit/main/.web/docs/:path`, 20 | text: 'Suggest changes to this page' 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /.web/docs/team.md: -------------------------------------------------------------------------------- 1 | --- 2 | layout: page 3 | title: Join the Team 4 | description: The development of Connect is guided by an international team. 5 | --- 6 | 7 | 8 | -------------------------------------------------------------------------------- /.web/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "connect-minekube-docs", 3 | "type": "module", 4 | "version": "1.0.0", 5 | "author": "Minekube", 6 | "scripts": { 7 | "dev": "vitepress dev docs", 8 | "build": "vitepress build docs", 9 | "serve": "vitepress serve docs" 10 | }, 11 | "devDependencies": { 12 | "autoprefixer": "^10.4.13", 13 | "feed": "^4.2.2", 14 | "postcss": "^8.4.35", 15 | "tailwindcss": "^3.2.4", 16 | "vitepress": "^1.6.3", 17 | "vue": "^3.3.4" 18 | }, 19 | "postcss": { 20 | "plugins": { 21 | "tailwindcss": {}, 22 | "autoprefixer": {} 23 | } 24 | }, 25 | "dependencies": { 26 | "globe.gl": "^2.41.4", 27 | "posthog-js": "^1.245.1", 28 | "three": "^0.176.0" 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /.web/tailwind.config.js: -------------------------------------------------------------------------------- 1 | /** @type {import('tailwindcss').Config} */ 2 | export default { 3 | content: ['./docs/.vitepress/**/*.{js,ts,vue}'], 4 | safelist: ['html', 'body'], 5 | darkMode: 'class', 6 | } 7 | 8 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | all: fmt vet mod lint 2 | 3 | # Run tests 4 | test: fmt vet 5 | go test ./... 6 | 7 | # Run go fmt against code 8 | fmt: 9 | go fmt ./... 10 | 11 | # Run go fmt against code 12 | mod: 13 | go mod tidy && go mod verify 14 | 15 | # Run go vet against code 16 | vet: 17 | go vet ./... 18 | 19 | # Run linters 20 | lint: 21 | go run github.com/golangci/golangci-lint/cmd/golangci-lint@latest run 22 | 23 | # Serve the docs website locally and auto on changes 24 | dev-docs: 25 | cd .web && yarn install && yarn dev 26 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | [![Logo](.web/docs/public/og-image.png)](https://connect.minekube.com) 2 | 3 | # Minekube Connect 4 | 5 | [![GitHub release (latest SemVer)](https://img.shields.io/github/v/release/minekube/connect?sort=semver)](https://github.com/minekube/connect/releases) 6 | [![Doc](https://img.shields.io/badge/go.dev-reference-007d9c?logo=go)](https://pkg.go.dev/go.minekube.com/connect) 7 | [![GitHub go.mod Go version](https://img.shields.io/github/go-mod/go-version/minekube/connect?logo=go)](https://golang.org/doc/devel/release.html) 8 | [![Go Report Card](https://goreportcard.com/badge/go.minekube.com/connect)](https://goreportcard.com/report/go.minekube.com/connect) 9 | [![test](https://github.com/minekube/connect/workflows/test/badge.svg)](https://github.com/minekube/connect/actions?query=workflow%3Atest) 10 | [![Discord](https://img.shields.io/discord/633708750032863232?logo=discord)](https://discord.gg/6vMDqWE) 11 | 12 | **Connect** allows you to connect any Minecraft server, 13 | whether it's in online mode, public, behind your protected home network or anywhere else in the world, 14 | with our highly available, performant and low latency edge proxies network nearest to you. 15 | 16 | **Browse active servers now! Join `minekube.net` with your Minecraft client!** 17 | 18 | > Note that the [client is open source](https://github.com/minekube/connect-java), but not the server side production service. 19 | 20 | ## Features 21 | 22 | - [x] ProtoBuf typed 23 | - [x] Streaming transport protocols 24 | - [x] WebSocket support 25 | - equally or more efficient than gRPC 26 | - better web proxy support: cloudflared, nginx, ... 27 | - [ ] gRPC support (improved developer experience) 28 | - No immediate support planned, [see](internal/grpc) 29 | - [x] Minekube Connect plugin support for: 30 | - [x] [Gate](https://github.com/minekube/gate) 31 | - [x] [Spigot/PaperMC](https://github.com/minekube/connect-java) 32 | - [x] [Velocity](https://github.com/minekube/connect-java) 33 | - [x] [BungeeCord](https://github.com/minekube/connect-java) 34 | - [ ] Sponge 35 | - [ ] Minestom 36 | - [x] Client side service tooling in Go 37 | - [x] Server side service tooling in Go 38 | - [x] Client- & service-side tests implementation in Go 39 | - [x] Awesome [documentation website](https://connect.minekube.com/) 40 | -------------------------------------------------------------------------------- /aliases.go: -------------------------------------------------------------------------------- 1 | package connect 2 | 3 | import ( 4 | "google.golang.org/genproto/googleapis/rpc/status" 5 | 6 | pb "buf.build/gen/go/minekube/connect/protocolbuffers/go/minekube/connect/v1alpha1" 7 | ) 8 | 9 | // Type alias to better support updating versions. 10 | // See the referenced type for documentation. 11 | // 12 | // Other go files should only ever use the provided 13 | // alias type and never import a specific proto version. 14 | type ( 15 | Session = pb.Session 16 | Authentication = pb.Authentication 17 | 18 | SessionRejection = pb.SessionRejection 19 | RejectionReason = status.Status // The reason why a session proposal is rejected. 20 | 21 | Player = pb.Player 22 | GameProfile = pb.GameProfile 23 | GameProfileProperty = pb.GameProfileProperty 24 | 25 | WatchRequest = pb.WatchRequest 26 | WatchResponse = pb.WatchResponse 27 | ) 28 | -------------------------------------------------------------------------------- /connect.go: -------------------------------------------------------------------------------- 1 | package connect 2 | 3 | import ( 4 | "context" 5 | "net" 6 | 7 | "go.minekube.com/connect/internal/ctxkey" 8 | ) 9 | 10 | // Well-known headers / metadata keys 11 | const ( 12 | MDPrefix = "connect-" // The prefix of Connect metadata keys. 13 | MDSession = MDPrefix + "session" // Metadata key specifying the session id for a Tunnel. 14 | MDEndpoint = MDPrefix + "endpoint" // Metadata key specifying the watching Endpoint. 15 | ) 16 | 17 | // Tunnel represents an outbound only tunnel initiated by 18 | // an Endpoint for a specific SessionProposal. 19 | type Tunnel net.Conn 20 | 21 | // Tunneler creates a Tunnel. 22 | type Tunneler interface { 23 | Tunnel(context.Context) (Tunnel, error) 24 | } 25 | 26 | // Watcher registers the calling endpoint and watches for sessions 27 | // proposed by the WatchService. To stop watching cancel the context. 28 | // If ReceiveProposal returns a non-nil non-EOF error Watch returns it. 29 | type Watcher interface { 30 | Watch(context.Context, ReceiveProposal) error 31 | } 32 | 33 | // ReceiveProposal is called when Watcher receives a SessionProposal. 34 | type ReceiveProposal func(proposal SessionProposal) error 35 | 36 | // SessionProposal specifies an incoming session proposal. 37 | // Use the Session to create the connection tunnel or reject the session with an optional reason. 38 | type SessionProposal interface { 39 | Session() *Session // The session proposed to connect to the Endpoint. 40 | Reject(context.Context, *RejectionReason) error // Rejects the session proposal with an optional reason. 41 | } 42 | 43 | // Endpoint is an endpoint that listens for 44 | // sessions to either reject them or establish 45 | // a tunnel for receiving the connection. 46 | type Endpoint interface { 47 | Watcher 48 | Tunneler 49 | } 50 | 51 | // TunnelListener is a network listener for tunnel connections. 52 | type TunnelListener interface { 53 | AcceptTunnel(context.Context, Tunnel) error 54 | } 55 | 56 | // EndpointListener is a network listener for endpoint watches. 57 | type EndpointListener interface { 58 | AcceptEndpoint(context.Context, EndpointWatch) error 59 | } 60 | 61 | // EndpointWatch is a watching Endpoint. 62 | type EndpointWatch interface { 63 | // Propose proposes a session to the Endpoint. 64 | // The Endpoint either rejects the proposal or initiates 65 | // a Tunnel to receive the session connection. 66 | Propose(context.Context, *Session) error 67 | Rejections() <-chan *SessionRejection // Rejections receives rejected session proposals from the Endpoint. 68 | } 69 | 70 | // Listener listens for watching endpoints and tunnel connections from endpoints. 71 | type Listener interface { 72 | EndpointListener 73 | TunnelListener 74 | } 75 | 76 | // TunnelOptions are options for Tunneler and TunnelListener. 77 | // Use WithTunnelOptions to propagate TunnelOptions in context. 78 | type TunnelOptions struct { 79 | // LocalAddr fakes the local address of the Tunnel when specified. 80 | // 81 | // If this TunnelOptions destine to Tunneler 82 | // it is recommended to use the Endpoint address/name. 83 | // 84 | // If this TunnelOptions destine to TunnelListener 85 | // it is recommended to use the underlying network listener address. 86 | LocalAddr net.Addr 87 | // RemoteAddr fakes the remote address of the Tunnel when specified. 88 | // 89 | // If this TunnelOptions destine to Tunneler 90 | // it is recommended to use the underlying connection remote address. 91 | // 92 | // If this TunnelOptions destine to TunnelListener 93 | // it is recommended to use the Endpoint address/name. 94 | RemoteAddr net.Addr // It is recommended to use the player address. 95 | } 96 | 97 | // WithTunnelOptions stores TunnelOptions in a context. 98 | func WithTunnelOptions(ctx context.Context, opts TunnelOptions) context.Context { 99 | return context.WithValue(ctx, ctxkey.TunnelOptions{}, opts) 100 | } 101 | 102 | // Addr is an address in the "connect" network. 103 | type Addr string 104 | 105 | func (a Addr) String() string { return string(a) } 106 | func (a Addr) Network() string { return "connect" } 107 | -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module go.minekube.com/connect 2 | 3 | go 1.21.1 4 | 5 | require ( 6 | buf.build/gen/go/minekube/connect/protocolbuffers/go v1.32.0-20230517110945-04c17e7d2fd9.1 7 | github.com/coder/websocket v1.8.12 8 | github.com/stretchr/testify v1.8.1 9 | google.golang.org/genproto/googleapis/rpc v0.0.0-20231212172506-995d672761c0 10 | google.golang.org/grpc v1.59.0 11 | google.golang.org/protobuf v1.32.0 12 | ) 13 | 14 | require ( 15 | github.com/davecgh/go-spew v1.1.1 // indirect 16 | github.com/golang/protobuf v1.5.3 // indirect 17 | github.com/kr/pretty v0.3.0 // indirect 18 | github.com/pmezard/go-difflib v1.0.0 // indirect 19 | github.com/rogpeppe/go-internal v1.9.0 // indirect 20 | golang.org/x/net v0.17.0 // indirect 21 | gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c // indirect 22 | gopkg.in/yaml.v3 v3.0.1 // indirect 23 | ) 24 | -------------------------------------------------------------------------------- /go.sum: -------------------------------------------------------------------------------- 1 | buf.build/gen/go/minekube/connect/protocolbuffers/go v1.32.0-20230517110945-04c17e7d2fd9.1 h1:+uYVRAyB2XYZ7hgREHRUY4XAAS5X4e6lY7/ZMDh4a+4= 2 | buf.build/gen/go/minekube/connect/protocolbuffers/go v1.32.0-20230517110945-04c17e7d2fd9.1/go.mod h1:rxISByFKweLOwpKBVtkRWTvyGMp+oNxHwnitXXiboE0= 3 | github.com/coder/websocket v1.8.12 h1:5bUXkEPPIbewrnkU8LTCLVaxi4N4J8ahufH2vlo4NAo= 4 | github.com/coder/websocket v1.8.12/go.mod h1:LNVeNrXQZfe5qhS9ALED3uA+l5pPqvwXg3CKoDBB2gs= 5 | github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= 6 | github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 7 | github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= 8 | github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 9 | github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= 10 | github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg= 11 | github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= 12 | github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= 13 | github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= 14 | github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= 15 | github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= 16 | github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= 17 | github.com/kr/pretty v0.3.0 h1:WgNl7dwNpEZ6jJ9k1snq4pZsg7DOEN8hP9Xw0Tsjwk0= 18 | github.com/kr/pretty v0.3.0/go.mod h1:640gp4NfQd8pI5XOwp5fnNeVWj67G7CFk/SaSQn7NBk= 19 | github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= 20 | github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= 21 | github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= 22 | github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= 23 | github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= 24 | github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= 25 | github.com/rogpeppe/go-internal v1.6.1/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc= 26 | github.com/rogpeppe/go-internal v1.9.0 h1:73kH8U+JUqXU8lRuOHeVHaa/SZPifC7BkcraZVejAe8= 27 | github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs= 28 | github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= 29 | github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= 30 | github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= 31 | github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= 32 | github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= 33 | github.com/stretchr/testify v1.8.1 h1:w7B6lhMri9wdJUVmEZPGGhZzrYTPvgJArz7wNPgYKsk= 34 | github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= 35 | golang.org/x/net v0.17.0 h1:pVaXccu2ozPjCXewfr1S7xza/zcXTity9cCdXQYSjIM= 36 | golang.org/x/net v0.17.0/go.mod h1:NxSsAGuq816PNPmqtQdLE42eU2Fs7NoRIZrHJAlaCOE= 37 | golang.org/x/sys v0.13.0 h1:Af8nKPmuFypiUBjVoU9V20FiaFXOcuZI21p0ycVYYGE= 38 | golang.org/x/sys v0.13.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 39 | golang.org/x/text v0.13.0 h1:ablQoSUd0tRdKxZewP80B+BaqeKJuVhuRxj/dkrun3k= 40 | golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= 41 | golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 42 | google.golang.org/genproto/googleapis/rpc v0.0.0-20231212172506-995d672761c0 h1:/jFB8jK5R3Sq3i/lmeZO0cATSzFfZaJq1J2Euan3XKU= 43 | google.golang.org/genproto/googleapis/rpc v0.0.0-20231212172506-995d672761c0/go.mod h1:FUoWkonphQm3RhTS+kOEhF8h0iDpm4tdXolVCeZ9KKA= 44 | google.golang.org/grpc v1.59.0 h1:Z5Iec2pjwb+LEOqzpB2MR12/eKFhDPhuqW91O+4bwUk= 45 | google.golang.org/grpc v1.59.0/go.mod h1:aUPDwccQo6OTjy7Hct4AfBPD1GptF4fyUjIkQ9YtF98= 46 | google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= 47 | google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= 48 | google.golang.org/protobuf v1.32.0 h1:pPC6BG5ex8PDFnkbrGU3EixyhKcQ2aDuBS36lqK/C7I= 49 | google.golang.org/protobuf v1.32.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos= 50 | gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 51 | gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 52 | gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= 53 | gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= 54 | gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= 55 | gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= 56 | gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= 57 | gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= 58 | -------------------------------------------------------------------------------- /internal/ctxkey/keys.go: -------------------------------------------------------------------------------- 1 | package ctxkey 2 | 3 | type TunnelOptions struct{} 4 | -------------------------------------------------------------------------------- /internal/ctxutil/util.go: -------------------------------------------------------------------------------- 1 | package ctxutil 2 | 3 | import ( 4 | "context" 5 | 6 | "google.golang.org/grpc/peer" 7 | 8 | "go.minekube.com/connect" 9 | "go.minekube.com/connect/internal/ctxkey" 10 | ) 11 | 12 | func tunnelOptions(ctx context.Context) connect.TunnelOptions { 13 | opts, _ := ctx.Value(ctxkey.TunnelOptions{}).(connect.TunnelOptions) 14 | return opts 15 | } 16 | 17 | func TunnelOptionsOrDefault(ctx context.Context) connect.TunnelOptions { 18 | opts := tunnelOptions(ctx) 19 | if opts.LocalAddr == nil { 20 | opts.LocalAddr = connect.Addr("unknown") 21 | } 22 | if opts.RemoteAddr == nil { 23 | if p, ok := peer.FromContext(ctx); ok { 24 | opts.RemoteAddr = p.Addr 25 | } else { 26 | opts.RemoteAddr = connect.Addr("unknown") 27 | } 28 | } 29 | return opts 30 | } 31 | -------------------------------------------------------------------------------- /internal/netutil/util.go: -------------------------------------------------------------------------------- 1 | package netutil 2 | 3 | import ( 4 | "fmt" 5 | "net" 6 | ) 7 | 8 | type Conn struct { 9 | net.Conn 10 | CloseFn func() error 11 | VLocalAddr, VRemoteAddr net.Addr 12 | } 13 | 14 | func (c *Conn) Close() error { return c.CloseFn() } 15 | func (c *Conn) LocalAddr() net.Addr { return c.VLocalAddr } 16 | func (c *Conn) RemoteAddr() net.Addr { return c.VRemoteAddr } 17 | func (c *Conn) String() string { 18 | if s, ok := c.Conn.(fmt.Stringer); ok { 19 | return s.String() 20 | } 21 | return fmt.Sprintf("Tunnel(remote=%s via %s, local=%s via %s)", 22 | c.RemoteAddr().String(), c.RemoteAddr().Network(), 23 | c.LocalAddr().String(), c.LocalAddr().Network()) 24 | } 25 | -------------------------------------------------------------------------------- /internal/testutil/suite.go: -------------------------------------------------------------------------------- 1 | package testutil 2 | 3 | import ( 4 | "context" 5 | "fmt" 6 | "io" 7 | "sync/atomic" 8 | "time" 9 | 10 | "github.com/stretchr/testify/suite" 11 | "google.golang.org/grpc/codes" 12 | "google.golang.org/grpc/status" 13 | 14 | "go.minekube.com/connect" 15 | ) 16 | 17 | type Suite struct { 18 | suite.Suite 19 | 20 | Endpoint connect.Endpoint 21 | StartWatchServer func(ctx context.Context, ln connect.EndpointListener) error 22 | StartTunnelServer func(ctx context.Context, ln connect.TunnelListener) error 23 | } 24 | 25 | func (suite *Suite) SetupTest() { 26 | suite.NotNil(suite.Endpoint) 27 | suite.NotNil(suite.StartWatchServer) 28 | } 29 | 30 | func (suite *Suite) TestWatchReject() { 31 | proposal := &connect.Session{Id: "abc"} 32 | rejection := status.New(codes.Aborted, "don't want this").Proto() 33 | 34 | var expSeq = []string{ 35 | "s: got watcher", 36 | "c: got proposal " + proposal.GetId(), 37 | "c: rejection sent", 38 | "s: got rejection " + rejection.String(), 39 | } 40 | var seq sequence 41 | 42 | ctx, stop := context.WithTimeout(context.TODO(), time.Second*3) 43 | 44 | ln := acceptEndpoint(func(ctx context.Context, watch connect.EndpointWatch) error { 45 | seq.Add("s: got watcher") 46 | suite.NoError(watch.Propose(ctx, proposal)) 47 | for rej := range watch.Rejections() { 48 | seq.Add("s: got rejection " + rej.GetReason().String()) 49 | break 50 | } 51 | time.Sleep(time.Millisecond * 100) // let "c: rejection sent" 52 | 53 | return nil 54 | }) 55 | 56 | go func() { suite.NoError(suite.StartWatchServer(ctx, ln)) }() 57 | 58 | time.Sleep(time.Millisecond * 100) // Wait for server 59 | go func() { 60 | err := suite.Endpoint.Watch(ctx, func(proposal connect.SessionProposal) error { 61 | seq.Add("c: got proposal " + proposal.Session().GetId()) 62 | suite.NoError(proposal.Reject(ctx, rejection)) 63 | seq.Add("c: rejection sent") 64 | return nil 65 | }) 66 | suite.NotNil(err) 67 | suite.Contains(err.Error(), "closed serverside") 68 | stop() 69 | }() 70 | 71 | <-ctx.Done() 72 | suite.Assert().ErrorIs(ctx.Err(), context.Canceled) 73 | suite.Assert().Equal(expSeq, seq.Get()) 74 | } 75 | 76 | func (suite *Suite) TestTunnel() { 77 | ctx, stop := context.WithTimeout(context.TODO(), time.Second*3) 78 | 79 | toClientMsg := []byte("hello client") 80 | toServerMsg := []byte("hello server") 81 | 82 | var expSeq = []string{ 83 | "c: tunnel opened Tunnel(remote=unknown via connect, local=unknown via connect)", 84 | "s: got tunnel Tunnel(remote=unknown via connect, local=unknown via connect)", 85 | "s: read", 86 | "c: read", 87 | "c: no read", 88 | } 89 | var seq sequence 90 | 91 | ln := acceptTunnel(func(ctx context.Context, tunnel connect.Tunnel) error { 92 | time.Sleep(time.Millisecond * 100) // let "c: tunnel opened" 93 | seq.Add("s: got tunnel " + fmt.Sprint(tunnel)) 94 | 95 | // client -> server 96 | b := make([]byte, 100) 97 | n, err := tunnel.Read(b) 98 | suite.NoError(err) 99 | suite.Equal(len(toServerMsg), n) 100 | suite.Equal(toServerMsg, b[:n]) 101 | seq.Add("s: read") 102 | 103 | // client <- server 104 | n, err = tunnel.Write(toClientMsg) 105 | suite.NoError(err) 106 | suite.Equal(len(toClientMsg), n) 107 | 108 | // Close server side 109 | suite.NoError(tunnel.Close()) 110 | return nil 111 | }) 112 | 113 | go func() { suite.NoError(suite.StartTunnelServer(ctx, ln)) }() 114 | 115 | time.Sleep(time.Millisecond * 100) // Wait for server 116 | go func() { 117 | tunnel, err := suite.Endpoint.Tunnel(ctx) 118 | suite.NoError(err) 119 | seq.Add("c: tunnel opened " + fmt.Sprint(tunnel)) 120 | 121 | // client -> server 122 | n, err := tunnel.Write(toServerMsg) 123 | suite.NoError(err) 124 | suite.Equal(len(toServerMsg), n) 125 | 126 | // client <- server 127 | b := make([]byte, 100) 128 | n, err = tunnel.Read(b) 129 | suite.NoError(err) 130 | suite.Equal(len(toClientMsg), n) 131 | suite.Equal(toClientMsg, b[:n]) 132 | seq.Add("c: read") 133 | 134 | // Should be closed server side by now 135 | b = make([]byte, 100) 136 | for i := 0; i < 5; i++ { 137 | n, err = tunnel.Read(b) 138 | suite.Empty(n) 139 | if err == nil { 140 | continue // retry 141 | } 142 | } 143 | suite.ErrorIs(err, io.EOF) 144 | seq.Add("c: no read") 145 | 146 | _ = tunnel.Close() 147 | stop() 148 | }() 149 | 150 | <-ctx.Done() 151 | suite.Assert().ErrorIs(ctx.Err(), context.Canceled) 152 | suite.Assert().Equal(expSeq, seq.Get()) 153 | } 154 | 155 | type sequence struct { 156 | v atomic.Value 157 | } 158 | 159 | func (s *sequence) Add(str string) { 160 | s.v.Store(append(s.Get(), str)) 161 | } 162 | 163 | func (s *sequence) Get() []string { 164 | str, _ := s.v.Load().([]string) 165 | return str 166 | } 167 | 168 | type acceptEndpoint func(ctx context.Context, watch connect.EndpointWatch) error 169 | 170 | func (fn acceptEndpoint) AcceptEndpoint(ctx context.Context, watch connect.EndpointWatch) error { 171 | return fn(ctx, watch) 172 | } 173 | 174 | type acceptTunnel func(ctx context.Context, tunnel connect.Tunnel) error 175 | 176 | func (fn acceptTunnel) AcceptTunnel(ctx context.Context, tunnel connect.Tunnel) error { 177 | return fn(ctx, tunnel) 178 | } 179 | -------------------------------------------------------------------------------- /internal/util/util.go: -------------------------------------------------------------------------------- 1 | package util 2 | 3 | import ( 4 | "context" 5 | "errors" 6 | 7 | "go.minekube.com/connect" 8 | ) 9 | 10 | type SessionProposal struct { 11 | Proposal *connect.Session 12 | RejectFn func(ctx context.Context, r *connect.RejectionReason) error 13 | } 14 | 15 | func (p *SessionProposal) String() string { 16 | return p.Proposal.String() 17 | } 18 | 19 | func (p *SessionProposal) Session() *connect.Session { 20 | return p.Proposal 21 | } 22 | 23 | func (p *SessionProposal) Reject(ctx context.Context, r *connect.RejectionReason) error { 24 | return p.RejectFn(ctx, r) 25 | } 26 | 27 | type EndpointWatch struct { 28 | ProposeFn func(ctx context.Context, session *connect.Session) error 29 | RejectionsChan chan *connect.SessionRejection 30 | 31 | Receive func() (*connect.WatchRequest, error) 32 | } 33 | 34 | func (w *EndpointWatch) Propose(ctx context.Context, session *connect.Session) error { 35 | if session == nil { 36 | return errors.New("session must not be nil") 37 | } 38 | if session.GetId() == "" { 39 | return errors.New("missing session id in proposal") 40 | } 41 | return w.ProposeFn(ctx, session) 42 | } 43 | func (w *EndpointWatch) Rejections() <-chan *connect.SessionRejection { 44 | return w.RejectionsChan 45 | } 46 | 47 | func (w *EndpointWatch) StartReceiveRejections(ctx context.Context) { 48 | defer close(w.RejectionsChan) 49 | for { 50 | req, err := w.Receive() 51 | if err != nil { 52 | return // drop error 53 | } 54 | if req.GetSessionRejection() == nil { 55 | continue // Unexpected 56 | } 57 | select { 58 | case w.RejectionsChan <- req.GetSessionRejection(): 59 | case <-ctx.Done(): 60 | return // stream closed, done 61 | } 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /internal/wspb/wspb.go: -------------------------------------------------------------------------------- 1 | // Package wspb provides helpers for reading and writing protobuf messages. 2 | package wspb 3 | 4 | import ( 5 | "bytes" 6 | "context" 7 | "fmt" 8 | "sync" 9 | 10 | "github.com/coder/websocket" 11 | "google.golang.org/protobuf/proto" 12 | ) 13 | 14 | // Read reads a protobuf message from c into v. 15 | // It will reuse buffers in between calls to avoid allocations. 16 | func Read(ctx context.Context, c *websocket.Conn, v proto.Message) error { 17 | return read(ctx, c, v) 18 | } 19 | 20 | func read(ctx context.Context, c *websocket.Conn, v proto.Message) (err error) { 21 | defer errdWrap(&err, "failed to read protobuf message") 22 | 23 | typ, r, err := c.Reader(ctx) 24 | if err != nil { 25 | return err 26 | } 27 | 28 | if typ != websocket.MessageBinary { 29 | c.Close(websocket.StatusUnsupportedData, "expected binary message") 30 | return fmt.Errorf("expected binary message for protobuf but got: %v", typ) 31 | } 32 | 33 | buf := bpoolGet() 34 | defer bpoolPut(buf) 35 | 36 | _, err = buf.ReadFrom(r) 37 | if err != nil { 38 | return err 39 | } 40 | 41 | err = proto.Unmarshal(buf.Bytes(), v) 42 | if err != nil { 43 | c.Close(websocket.StatusInvalidFramePayloadData, "failed to unmarshal protobuf") 44 | return fmt.Errorf("failed to unmarshal protobuf: %w", err) 45 | } 46 | 47 | return nil 48 | } 49 | 50 | // Write writes the protobuf message v to c. 51 | // It will reuse buffers in between calls to avoid allocations. 52 | func Write(ctx context.Context, c *websocket.Conn, v proto.Message) error { 53 | return write(ctx, c, v) 54 | } 55 | 56 | func write(ctx context.Context, c *websocket.Conn, v proto.Message) (err error) { 57 | defer errdWrap(&err, "failed to write protobuf message") 58 | 59 | b := bpoolGet() 60 | defer func() { bpoolPut(b) }() 61 | 62 | buf := b.Bytes() 63 | 64 | buf, err = proto.MarshalOptions{}.MarshalAppend(buf, v) 65 | if err != nil { 66 | return fmt.Errorf("failed to marshal protobuf: %w", err) 67 | } 68 | 69 | *b = *bytes.NewBuffer(buf) // reset buffer to new bytes 70 | 71 | return c.Write(ctx, websocket.MessageBinary, buf) 72 | } 73 | 74 | // errdWrap wraps err with fmt.Errorf if err is non nil. 75 | // Intended for use with defer and a named error return. 76 | // Inspired by https://github.com/golang/go/issues/32676. 77 | func errdWrap(err *error, f string, v ...interface{}) { 78 | if *err != nil { 79 | *err = fmt.Errorf(f+": %w", append(v, *err)...) 80 | } 81 | } 82 | 83 | var bpool sync.Pool 84 | 85 | // Get returns a buffer from the pool or creates a new one if 86 | // the pool is empty. 87 | func bpoolGet() *bytes.Buffer { 88 | b := bpool.Get() 89 | if b == nil { 90 | return bytes.NewBuffer(nil) 91 | } 92 | return b.(*bytes.Buffer) 93 | } 94 | 95 | // Put returns a buffer into the pool. 96 | func bpoolPut(b *bytes.Buffer) { 97 | b.Reset() 98 | bpool.Put(b) 99 | } 100 | -------------------------------------------------------------------------------- /renovate.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://docs.renovatebot.com/renovate-schema.json", 3 | "extends": [ 4 | "config:base" 5 | ] 6 | } 7 | -------------------------------------------------------------------------------- /ws/client.go: -------------------------------------------------------------------------------- 1 | package ws 2 | 3 | import ( 4 | "context" 5 | "errors" 6 | "io" 7 | "net/http" 8 | 9 | "github.com/coder/websocket" 10 | "go.minekube.com/connect/internal/wspb" 11 | 12 | "go.minekube.com/connect" 13 | "go.minekube.com/connect/internal/ctxutil" 14 | "go.minekube.com/connect/internal/netutil" 15 | "go.minekube.com/connect/internal/util" 16 | ) 17 | 18 | // ClientOptions for Watch and Tunnel, implementing connect.Tunneler. 19 | type ClientOptions struct { 20 | URL string // The URL of the WebSocket server 21 | DialContext context.Context // Optional WebSocket dial context 22 | DialOptions websocket.DialOptions // Optional WebSocket dial options 23 | Handshake HandshakeResponse // Optionally run after successful WebSocket handshake 24 | } 25 | 26 | // HandshakeResponse is called after receiving the 27 | // WebSocket handshake response from the server. 28 | type HandshakeResponse func(ctx context.Context, res *http.Response) (context.Context, error) 29 | 30 | // Tunnel implements connect.Tunneler and creates a connection over a WebSocket. 31 | // On error a http.Response may be provided by DialErrorResponse. 32 | func (o ClientOptions) Tunnel(ctx context.Context) (connect.Tunnel, error) { 33 | ctx, ws, err := o.dial(ctx) 34 | if err != nil { 35 | return nil, err 36 | } 37 | 38 | // Extract additional options 39 | opts := ctxutil.TunnelOptionsOrDefault(ctx) 40 | 41 | // Return connection 42 | ctx, cancel := context.WithCancel(ctx) 43 | wsConn := websocket.NetConn(ctx, ws, websocket.MessageBinary) 44 | return &netutil.Conn{ 45 | Conn: wsConn, 46 | CloseFn: func() error { cancel(); return wsConn.Close() }, 47 | VLocalAddr: opts.LocalAddr, 48 | VRemoteAddr: opts.RemoteAddr, 49 | }, nil 50 | } 51 | 52 | // Watch implements connect.Watcher and watches for session proposals. 53 | // On error a http.Response may be provided by DialErrorResponse. 54 | func (o ClientOptions) Watch(ctx context.Context, propose connect.ReceiveProposal) error { 55 | ctx, ws, err := o.dial(ctx) 56 | if err != nil { 57 | return err 58 | } 59 | // Watch for session proposals 60 | for { 61 | res := new(connect.WatchResponse) 62 | err = wspb.Read(ctx, ws, res) 63 | if err != nil { 64 | break 65 | } 66 | if res.GetSession() == nil { 67 | continue 68 | } 69 | id := res.GetSession().GetId() 70 | rejectFn := func(ctx context.Context, r *connect.RejectionReason) error { 71 | return wspb.Write(ctx, ws, &connect.WatchRequest{ 72 | SessionRejection: &connect.SessionRejection{ 73 | Id: id, 74 | Reason: r, 75 | }, 76 | }) 77 | } 78 | proposal := &util.SessionProposal{ 79 | Proposal: res.GetSession(), 80 | RejectFn: rejectFn, 81 | } 82 | if err = propose(proposal); err != nil { 83 | break 84 | } 85 | } 86 | _ = ws.Close(websocket.StatusNormalClosure, err.Error()) 87 | if errors.Is(err, io.EOF) { 88 | err = nil 89 | } 90 | return err 91 | } 92 | 93 | var _ connect.Tunneler = (*ClientOptions)(nil) 94 | var _ connect.Watcher = (*ClientOptions)(nil) 95 | -------------------------------------------------------------------------------- /ws/dial.go: -------------------------------------------------------------------------------- 1 | package ws 2 | 3 | import ( 4 | "context" 5 | "errors" 6 | "fmt" 7 | "net/http" 8 | 9 | "github.com/coder/websocket" 10 | "google.golang.org/grpc/metadata" 11 | ) 12 | 13 | // DialErrorResponse returns the HTTP response from the WebSocket handshake error, if any. 14 | func DialErrorResponse(err error) (*http.Response, bool) { 15 | var e *dialErr 16 | if errors.As(err, &e) { 17 | return e.res, true 18 | } 19 | return nil, false 20 | } 21 | 22 | func (o ClientOptions) dial(ctx context.Context) (context.Context, *websocket.Conn, error) { 23 | if o.URL == "" { 24 | return nil, nil, errors.New("missing websocket url") 25 | } 26 | 27 | header := metadata.Join( 28 | metadata.MD(o.DialOptions.HTTPHeader), 29 | fromOutgoingContext(o.DialContext), 30 | fromOutgoingContext(ctx), 31 | ) 32 | 33 | dialContext := o.DialContext 34 | if dialContext == nil { 35 | dialContext = ctx 36 | } 37 | 38 | // Dial service 39 | ws, res, err := websocket.Dial(dialContext, o.URL, &websocket.DialOptions{ 40 | HTTPClient: o.DialOptions.HTTPClient, 41 | HTTPHeader: http.Header(header), 42 | Subprotocols: o.DialOptions.Subprotocols, 43 | CompressionMode: o.DialOptions.CompressionMode, 44 | CompressionThreshold: o.DialOptions.CompressionThreshold, 45 | }) 46 | if err != nil { 47 | if res != nil { 48 | err = &dialErr{error: err, res: res} 49 | } 50 | return nil, nil, fmt.Errorf("error handshaking with websocket server: %w", err) 51 | } 52 | 53 | // Callback for handshake response 54 | if o.Handshake != nil { 55 | ctx, err = o.Handshake(ctx, res) 56 | if err != nil { 57 | _ = ws.Close(websocket.StatusNormalClosure, fmt.Sprintf( 58 | "handshake response rejected: %v", err)) 59 | return nil, nil, err 60 | } 61 | } 62 | 63 | return ctx, ws, nil 64 | } 65 | 66 | type dialErr struct { 67 | error 68 | res *http.Response 69 | } 70 | 71 | func (e *dialErr) Error() string { 72 | return fmt.Sprintf("%s (%d): %v", e.res.Status, e.res.StatusCode, e.error) 73 | } 74 | func (e *dialErr) Unwrap() error { return e.error } 75 | 76 | func fromOutgoingContext(ctx context.Context) metadata.MD { 77 | if ctx == nil { 78 | return nil 79 | } 80 | md, _ := metadata.FromOutgoingContext(ctx) 81 | return md 82 | } 83 | -------------------------------------------------------------------------------- /ws/server.go: -------------------------------------------------------------------------------- 1 | package ws 2 | 3 | import ( 4 | "context" 5 | "fmt" 6 | "net/http" 7 | "time" 8 | 9 | "github.com/coder/websocket" 10 | "go.minekube.com/connect/internal/wspb" 11 | "google.golang.org/grpc/metadata" 12 | 13 | "go.minekube.com/connect" 14 | "go.minekube.com/connect/internal/ctxutil" 15 | "go.minekube.com/connect/internal/netutil" 16 | "go.minekube.com/connect/internal/util" 17 | ) 18 | 19 | // ServerOptions for TunnelHandler and EndpointHandler. 20 | type ServerOptions struct { 21 | AcceptOptions websocket.AcceptOptions // Optional WebSocket accept options 22 | } 23 | 24 | // TunnelHandler returns a new http.Handler for accepting WebSocket requests for tunneling. 25 | func (o ServerOptions) TunnelHandler(ln connect.TunnelListener) http.Handler { 26 | fn := func(ctx context.Context, w http.ResponseWriter, r *http.Request) error { 27 | // Accept WebSocket 28 | ws, err := websocket.Accept(w, r, &o.AcceptOptions) 29 | if err != nil { 30 | return err 31 | } 32 | 33 | // Extract additional options 34 | opts := ctxutil.TunnelOptionsOrDefault(ctx) 35 | 36 | // Create tunnel from WebSocket 37 | ctx, cancel := context.WithCancel(ctx) 38 | wsConn := websocket.NetConn(ctx, ws, websocket.MessageBinary) 39 | conn := &netutil.Conn{ 40 | Conn: wsConn, 41 | CloseFn: func() error { cancel(); return wsConn.Close() }, 42 | VLocalAddr: opts.LocalAddr, 43 | VRemoteAddr: opts.RemoteAddr, 44 | } 45 | defer conn.Close() 46 | 47 | // Add http request to ctx 48 | ctx = withRequest(ctx, r) 49 | 50 | // Accept tunnel 51 | if err = ln.AcceptTunnel(ctx, conn); err != nil { 52 | // Specify meaningful close error 53 | _ = ws.Close(websocket.StatusProtocolError, fmt.Sprintf( 54 | "did not accept tunnel: %v", err)) 55 | return err 56 | } 57 | 58 | // Block handler until tunnel closure 59 | <-ctx.Done() 60 | _ = ws.Close(websocket.StatusNormalClosure, "closed serverside") 61 | return nil 62 | } 63 | 64 | return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 65 | // Dropping this error as http.Error(...) would be already called 66 | // at this point by our WebSocket library. 67 | _ = fn(r.Context(), w, r) 68 | }) 69 | } 70 | 71 | // EndpointHandler returns a new http.Handler for accepting WebSocket requests for watching endpoints. 72 | func (o ServerOptions) EndpointHandler(ln connect.EndpointListener) http.Handler { 73 | fn := func(ctx context.Context, w http.ResponseWriter, r *http.Request) error { 74 | // Accept WebSocket 75 | ws, err := websocket.Accept(w, r, &o.AcceptOptions) 76 | if err != nil { 77 | return err 78 | } 79 | 80 | // Prepare endpoint watch 81 | ctx, cancel := context.WithCancel(ctx) 82 | defer cancel() 83 | ew := &util.EndpointWatch{ 84 | ProposeFn: func(ctx context.Context, session *connect.Session) error { 85 | return wspb.Write(ctx, ws, &connect.WatchResponse{Session: session}) 86 | }, 87 | Receive: func() (*connect.WatchRequest, error) { 88 | req := new(connect.WatchRequest) 89 | return req, wspb.Read(ctx, ws, req) 90 | }, 91 | RejectionsChan: make(chan *connect.SessionRejection), 92 | } 93 | 94 | // Receive session rejections from endpoint 95 | go func() { ew.StartReceiveRejections(ctx); cancel() }() 96 | go pingLoop(ctx, pingInterval, ws) 97 | 98 | // Add http request to ctx 99 | ctx = withRequest(ctx, r) 100 | 101 | // Start blocking watch callback 102 | if err = ln.AcceptEndpoint(ctx, ew); err != nil { 103 | // Specify meaningful close error 104 | _ = ws.Close(websocket.StatusProtocolError, fmt.Sprintf( 105 | "did not accept endpoint: %v", err)) 106 | return err 107 | } 108 | 109 | _ = ws.Close(websocket.StatusNormalClosure, "closed serverside") 110 | return nil 111 | } 112 | return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 113 | // Dropping this error as http.Error(...) would be already called 114 | // at this point by our WebSocket library. 115 | _ = fn(r.Context(), w, r) 116 | }) 117 | } 118 | 119 | // RequestFromContext returns the accepted WebSocket request from the context. 120 | func RequestFromContext(ctx context.Context) *http.Request { 121 | r, _ := ctx.Value(httpRequestContextKey{}).(*http.Request) 122 | return r 123 | } 124 | 125 | const pingInterval = time.Second * 50 126 | 127 | // Send periodic pings to keep the WebSocket active since some web proxies 128 | // timeout the connection after 60-100 seconds. 129 | // https://community.cloudflare.com/t/cloudflare-websocket-timeout/5865/2 130 | func pingLoop(ctx context.Context, d time.Duration, ws *websocket.Conn) { 131 | t := time.NewTicker(d) 132 | defer t.Stop() 133 | for { 134 | select { 135 | case <-t.C: 136 | _ = ws.Ping(ctx) 137 | case <-ctx.Done(): 138 | return 139 | } 140 | } 141 | } 142 | 143 | type httpRequestContextKey struct{} 144 | 145 | func withRequest(ctx context.Context, r *http.Request) context.Context { 146 | // Add WebSocket handshake request header to metadata 147 | md, _ := metadata.FromIncomingContext(ctx) 148 | ctx = metadata.NewIncomingContext(ctx, metadata.Join(md, metadata.MD(r.Header))) 149 | 150 | return context.WithValue(ctx, httpRequestContextKey{}, r) 151 | } 152 | -------------------------------------------------------------------------------- /ws/suite_test.go: -------------------------------------------------------------------------------- 1 | package ws 2 | 3 | import ( 4 | "context" 5 | "net/http" 6 | "testing" 7 | 8 | "github.com/stretchr/testify/suite" 9 | 10 | "go.minekube.com/connect" 11 | "go.minekube.com/connect/internal/testutil" 12 | ) 13 | 14 | func TestSuite(t *testing.T) { 15 | suite.Run(t, &testutil.Suite{ 16 | Endpoint: ClientOptions{ 17 | URL: "ws://:8080", 18 | }, 19 | StartWatchServer: func(ctx context.Context, ln connect.EndpointListener) error { 20 | return startServer(ctx, ":8080", ServerOptions{}.EndpointHandler(ln)) 21 | }, 22 | StartTunnelServer: func(ctx context.Context, ln connect.TunnelListener) error { 23 | return startServer(ctx, ":8080", ServerOptions{}.TunnelHandler(ln)) 24 | }, 25 | }) 26 | } 27 | 28 | func startServer(ctx context.Context, addr string, handler http.Handler) error { 29 | svr := &http.Server{Addr: addr, Handler: handler} 30 | go func() { _ = svr.ListenAndServe() }() 31 | <-ctx.Done() 32 | return svr.Close() 33 | } 34 | -------------------------------------------------------------------------------- /yarn.lock: -------------------------------------------------------------------------------- 1 | # This file is generated by running "yarn install" inside your project. 2 | # Manual changes might be lost - proceed with caution! 3 | 4 | __metadata: 5 | version: 8 6 | cacheKey: 10c0 7 | 8 | "root-workspace-0b6124@workspace:.": 9 | version: 0.0.0-use.local 10 | resolution: "root-workspace-0b6124@workspace:." 11 | languageName: unknown 12 | linkType: soft 13 | --------------------------------------------------------------------------------