尚未偵測到更新。GitHub 發布版本後會顯示在這裡。
個人 GitHub 帳號下公開儲存庫的擁有者可以登入並驗證所有權,自行管理介紹,無需管理員核准。
登入並產生驗證檔案,在 30 分鐘內將其提交至此儲存庫的預設分支。其他分支或分叉儲存庫中的檔案無效。然後在下方檢查檔案,即可無需管理員核准直接管理專案。請保留檔案以維持管理權限。
如需登入,將前往登入頁面。登入後請回到此處,再次點擊所選的驗證按鈕。
在專案資料夾中執行 /nomorevibe verify,即可驗證所有權並自行管理介紹。
一分鐘完成驗證Website of Bannerlord Coop
翻譯準備中
取自儲存庫 README
正在載入完整 README。
檢視完整 README ↗Nightly builds are restricted to Testers and current Patreon, Boosty, and Afdian supporters, plus up to ten sponsored Discord accounts per eligible member. The installer is served by the private download gateway and verifies that entitlement on every run. See nightly-gateway/README.md for the security boundary and rollout order.
The official website for Bannerlord Co-op, a module that brings cooperative multiplayer to the Mount & Blade II: Bannerlord campaign.
By submitting a contribution to BannerlordCoop, you agree that your contribution may be used, modified, distributed, sublicensed, and relicensed by the BannerlordCoop project maintainers as part of the BannerlordCoop project.
You also confirm that you have the right to submit the contribution and that it does not knowingly include code copied from another project without permission.
Managed server owner/manager pages include a live-output panel that connects automatically. The browser connects only to the same-origin /api/servers/[serverId]/console route. That route revalidates the current Supabase user/session and forwards only the bearer token and validated server UUID to the fixed server-only CONTROL_PLANE_CONSOLE_ORIGIN; callers cannot select an upstream destination, actor, agent, process, or path.
The response is private, unbuffered SSE with a five-minute maximum session; the runner follows only new output (podman logs --follow --tail=0), so a connection never replays history and nothing is stored. The UI keeps one connection at a time and appends each connection's output to what it already shows: it renews an expired session, or a healthy stream that ends because the runner capped its log reader, at once while the tab is visible (or when the tab is shown again) for about half an hour without page activity, then pauses with a Reconnect button until the owner interacts again. Other closures use backoff bases of 2 s, 5 s, 10 s, 30 s and 60 s, each randomized within +/-25%, then independent uniform waits from 0 to 60 s so viewers recovering from a shared outage do not retry together. Exhausting the initial retries still signals lost-console guidance to Restart progress, while the console itself keeps reconnecting. Failed reconnects pause after half an hour without activity; hidden tabs defer reconnects until shown and refresh the inactivity allowance when shown again. The console waits while the server is stopped and reconnects as soon as the server is running again. Container-host logging and incomplete @DS@ control records are hidden. The UI retains at most 128 KiB and 2,000 lines. Production enablement still requires verified OpenNext/Cloudflare streaming and disconnect behavior plus the reviewed Oracle Caddy route; local mocks do not establish those deployment properties.
Each received chunk's decoded line events are appended together, and line/character retention runs once for that chunk. Each event still adds its trailing newline (including empty or multiline events), partial UTF-8/SSE frames wait for completion, and game phases are published in event order before the text update. The bounded history is still parsed when rendered, and scrolling back still pauses auto-follow. npx tsx --test src/app/components/servers/ManagedServerConsole.test.ts includes a full-buffer/100-event retention benchmark; it reports local synthetic timings, not production console latency.
Install dependencies:
npm install
Copy the example environment file and add your project values:
cp .env.example .env.local
YOUTUBE_API_KEY=your_api_key
NEXT_PUBLIC_SUPABASE_URL=https://your-project.supabase.co
NEXT_PUBLIC_SUPABASE_PUBLISHABLE_KEY=your_publishable_key
SUPABASE_SECRET_KEY=your_secret_key
[email protected]
Environment files are ignored by Git. Do not commit API keys or other secrets.
Start the development server:
npm run dev
Open http://localhost:3000.
npm run dev # Start the development server
npm run lint # Run ESLint
npm run build # Create a production build
npm run start # Start the production server
Before pushing changes, run:
npm run lint
npm run build
The /login page uses Supabase Auth for Google, Discord, and passwordless email sign-in.
.env.local.http://localhost:3000/auth/callback and your production /auth/callback URL to the Supabase redirect allow list under Authentication → URL Configuration.The OAuth providers also require their Supabase callback URL (shown in the provider settings) to be allow-listed in the Google/Discord developer console.
Signed-in page and server action requests pass through src/middleware.ts. It refreshes the Supabase session once it is within five minutes of expiry and forwards the new cookies, so the page or action normally finds nothing left to refresh. A refresh inside a server action writes cookies, and Next then re-renders the whole page in the action response; the managed server page's background polls used to do this. The middleware sends its cookies as plain Set-Cookie headers, because cookies set through response.cookies reach the action's cookie store and cause the same re-render. Route handlers (/api/* and the OAuth callbacks) skip it: their cookie writes re-render nothing, and the callbacks install sessions that a refresh would overwrite. open-next.config.ts lets a server action's cookies win over the middleware's, as in Next itself, so a sign-out or impersonation change on the same request still applies. The file keeps the deprecated middleware name because Next 16's proxy.ts runs only on Node.js, which OpenNext Cloudflare supports only experimentally.
Apply reviewed SQL migrations in their coordinated rollout order. Applied migration history remains immutable, including historical external-console tables and RPCs. The website no longer reads or writes those legacy objects; this source removal does not drop production data or rewrite Auth metadata.
The protected /admin page lists Supabase Auth users, searches by member information, and stores one of Admin, Server Manager, Standard Server, Premium Server, Developer, Helper, or User in each user's protected app_metadata.role.
To enable it:
SUPABASE_SECRET_KEY. Never use this value in a NEXT_PUBLIC_ variable or browser component.SUPABASE_ADMIN_EMAILS to the email address of the first administrator. Multiple bootstrap administrators may be comma-separated./admin.Bootstrap administrators always retain admin access, preventing an accidental total lockout. Assigned roles take effect after the user's Auth session refreshes or they sign in again.
The protected /admin/control-plane page is the administrative surface for managed hosting. It reads fleet/server/job/release/audit state and submits the administrator operations it offers through the control-plane-admin Supabase Edge Function. The page has no form for publishing the hosting-region catalog (set-hosting-regions); that operation is sent through the same Edge Function by hand, as described in docs/server-onboarding.md. The function reauthenticates the current Discord administrator and relays the typed request to the Oracle control-plane web adapter. The website does not query the private control-plane schema or talk directly to VPS providers, runners, containers, or object storage. See docs/control-plane-admin.md and the control-plane repository's docs/managed-hosting/web-admin.md.
The legacy placeholder implementation (not the current authenticated My Servers/onboarding flow below) provides these simulated behaviors:
Admin and Server Manager members can manage every placeholder hosted server and view its assigned account.Standard Server and Premium Server members see the placeholder management experience for their plan.0 4 * * *.The public directory and legacy placeholder assigned-server records remain fixtures. Typed placeholder records—including fictitious account assignments—live in src/app/lib/hosting/servers.ts; they are not evidence of current account assignments or onboarding eligibility.
The placeholder design is documented in docs/server-hosting-design.md.
The current /servers page loads authenticated managed assignments from the control plane using registered server UUIDs. New setup uses unused explicitly granted quota, not membership or role claims. Available regions create an assigned stopped server; full regions accept private durable requests through the existing authenticated my-servers Edge Function. The public directory loads the anonymous public-servers endpoint independently of private inventory.
See website server onboarding for the strict contract, exact-request recovery, Discord password controls, mock-only screenshots/tests and required schema → backend → my-servers Edge → UI rollout order. The Supabase hosting deployment workflow deploys affected managed-hosting APIs from main; schema and backend prerequisites retain their separate rollout gates.
My Servers uses authenticated control-plane assignments only. Manage a server using its registered /servers/<UUID> route. Unknown and retired server IDs return signed-in visitors to /servers; no static alias, IP address or display name is used to associate a legacy server with a managed record.
Owners and managers use the managed console, its command input, lifecycle controls and log downloads. Support and administrator assignments remain read-only and do not connect to console streaming. Owners rename a server from Settings through the control plane. Managed backup, save/configuration transfer, visibility, maintenance and release settings retain their existing authorization boundaries.
The Mods tab lets owners and managers reorder, enable, upload and delete custom modules. The browser calls only the same-origin /api/servers/<UUID>/mods route handlers, which revalidate the session and relay an allowlisted route (including 8 MiB binary upload chunks) through the my-servers Edge Function to the control plane's /api/v1/mods/* routes. See server mods for the transport, retry and archive rules and the rollout order.
The legacy external WebSocket console, catalog, owner/operator assignment actions, live rename actions, gateway, Docker node agent and deployment recipes have been removed; old /servers/live/<id> links redirect to /servers. This source change does not stop an existing deployment or erase historical account metadata.
Configure CONTROL_PLANE_CONSOLE_ORIGIN with the fixed HTTPS control-plane origin used by the /v1/user/console-stream proxy.
NEXT_PUBLIC_SUPABASE_URLPublic URL for the Supabase project used by the browser and server auth clients.
NEXT_PUBLIC_SUPABASE_PUBLISHABLE_KEYSupabase publishable key. Despite being browser-visible, row-level security and Auth policies must still protect project data. Never substitute the service-role key.
SUPABASE_SECRET_KEYServer-only Supabase secret key used by protected member-role administration to list users and update member roles. Never expose or commit it.
SUPABASE_ADMIN_EMAILSComma-separated bootstrap administrator emails. These users always have admin access and cannot be demoted through the member administration page.
CONTROL_PLANE_ORIGINOptional server-only origin for the website's direct control-plane reads: the administrator pane reads (/v1/admin/control-plane), owner inventory reads (/v1/user/control-plane) and the public directory (/v1/public/control-plane). Unset or empty uses the reviewed production origin https://control-plane.bannerlordcoop.com. An override must be a bare absolute https:// origin with no credentials, path, query or fragment, for example https://localhost:9443 for a local stack behind a trusted TLS proxy. Any other value fails closed: the read throws before a request is sent. The routes stay fixed in code; this value only selects the origin. It does not affect CONTROL_PLANE_CONSOLE_ORIGIN or the Edge Functions' CONTROL_PLANE_WEB_ORIGINS. Never use a NEXT_PUBLIC_ variable for it.
CONTROL_PLANE_CONSOLE_ORIGINServer-only bare HTTPS origin for the managed read-only console proxy, for example https://control-plane.example.com. The website reauthenticates the user/session and forwards only the requested registered server ID to /v1/user/console-stream. The retired CONSOLE_GATEWAY_URL and CONSOLE_SERVER_CATALOG variables are no longer read and can be removed from deployment settings separately.
YOUTUBE_API_KEYServer-only YouTube Data API v3 key used to retrieve:
The key is read only in src/app/lib/youtube.ts. Do not prefix it with NEXT_PUBLIC_, because that would expose it to browser code.
YouTube responses are cached for 24 hours.
The community statistic cards and server browser currently show an unavailable state. Their components and types are kept in place for the future game integration:
src/app/components/home/community/
src/app/components/utils/types/server.types.ts
The planned data flow is:
Bannerlord dedicated servers
→ central HTTPS registry API
→ Next.js website
Do not replace the unavailable state with mock production values. Live values should be introduced when the registry API contract and hosting are ready.
Official YouTube links and approved creator channel IDs are configured in:
src/app/components/home/media/CommunityMedia.tsx
Videos require only a YouTube URL:
{ href: "https://www.youtube.com/watch?v=VIDEO_ID" }
YouTube creators require only a channel ID:
{ channelId: "UC_CHANNEL_ID" }
The server-side YouTube utility retrieves the remaining metadata.
Creators on other platforms, such as Twitch, carry their full profile instead, because the site has no API integration for those platforms. Their avatar host must be allowed in next.config.ts:
{ profile: { id: "twitch:name", name: "Name", description: "", avatar: "https://...", href: "https://www.twitch.tv/name" } }
Creators appear in the order they are listed.
src/app/
├── components/
│ ├── home/ Homepage sections and interactive media
│ ├── layout/ Navbar and footer
│ ├── motion/ Shared animation components
│ └── utils/ Shared TypeScript types
├── lib/ Server-only integrations
├── globals.css Tailwind theme and global styles
├── layout.tsx Fonts and site metadata
└── page.tsx Homepage composition
Bannerlord Co-op is an independent community project and is not affiliated with or endorsed by TaleWorlds Entertainment.
Campaign deletion maps the control plane's save_still_loaded admission error to
localized guidance in all shipped locales: start the selected campaign, then stop
it before deleting the previously loaded campaign. Unknown codes keep the generic
failure message. The existing error and enqueue envelopes are unchanged. Durable
owner-visible delete-job outcomes remain tracked in ControlPlane #581.