Mine Yapping

Self-hosting & development

Run the backend yourself, build the mod from source, and point the mod at your own server.

Everything is in one repository: github.com/JoniJuntto/mine-yapping. Self-hosting is free and needs no purchase — you bring your own provider keys and there is no allowance, no credits, and no payment involved.

mine-yapping/
├── apps/
│   ├── server/       # Elysia API — conversation endpoint + auth
│   ├── admin/        # Web app: landing, dashboard, admin pages
│   └── fumadocs/     # This documentation site
├── packages/         # auth, db, env, shared config
├── minecraft-mod/    # Fabric client mod (Java/Gradle, not in the Bun workspace)
└── docker-compose.yml

You need Bun, PostgreSQL, and — for the mod — nothing but the Gradle wrapper, which downloads its own JDK 21.

Backend

Install and configure

bun install

Create apps/server/.env. All of these are required — the process refuses to start if any is missing or malformed, rather than failing later at request time:

VariableNotes
DATABASE_URLPostgreSQL connection string
BETTER_AUTH_SECRETmin. 32 characters
BETTER_AUTH_URLe.g. http://localhost:31415
RESEND_API_KEYverification and password-reset email
AUTH_EMAIL_FROMverified sender, e.g. Mine Yapping <auth@example.com>
TWITCH_CLIENT_IDTwitch application client ID
TWITCH_CLIENT_SECRETTwitch application client secret
POLAR_ACCESS_TOKENany non-empty string works locally
POLAR_WEBHOOK_SECRETPolar webhook signing secret
POLAR_SUCCESS_URLvalid URL
POLAR_SERVERoptional: sandbox (default) or production
CORS_ORIGINvalid URL, e.g. http://localhost:4001
OPENAI_API_KEYrequired for /api/converse
ELEVENLABS_API_KEYrequired for reply speech
DISABLE_SIGN_UPoptional; true for an invite-only setup
NODE_ENVoptional, defaults to development

Set SKIP_ENV_VALIDATION=1 to bypass validation temporarily (e.g. to run only tests).

Register ${BETTER_AUTH_URL}/api/auth/callback/twitch as the OAuth redirect URL for your Twitch application.

Migrate the database

bun run db:migrate

Run it

bun run dev:server     # API on http://localhost:31415
bun run dev:admin      # web app on http://localhost:4001
bun run dev            # everything
curl http://localhost:31415/
# → OK

Create the first admin

With DISABLE_SIGN_UP unset or false:

curl -X POST http://localhost:31415/api/auth/sign-up/email \
  -H 'Content-Type: application/json' \
  -d '{"name":"Admin","email":"admin@example.com","password":"change-me-now"}'

Then promote it once in PostgreSQL and sign in again to refresh the session:

UPDATE "user" SET role = 'admin' WHERE email = 'admin@example.com';

API surface

RoutePurpose
GET /Health check, returns OK
POST /api/converseMultipart compatibility endpoint; returns raw PCM with base64url transcript/reply headers
WS /api/converse/streamRealtime PCM in, transcript/reply control messages and binary PCM out — what the mod uses
GET /api/me/languageThe account's language, polled by the mod

/api/converse requires a hashed, revocable API key in the x-api-key header.

Port 31415 is fixed. If something else holds it the server exits with EADDRINUSE — check with lsof -i :31415.

Testing without Minecraft

# validation: missing fields are rejected before any provider call
curl -i -X POST http://localhost:31415/api/converse
# → HTTP 422

# full round trip with a real recording
curl -D response.headers -o reply.pcm -X POST http://localhost:31415/api/converse \
  -H 'x-api-key: my_YOUR_DASHBOARD_KEY' \
  -F audio=@speech.wav \
  -F entityId=test-uuid \
  -F entityType=minecraft:cow \
  -F entityName=Cow \
  -F playerName=joni \
  -F dimension=minecraft:overworld \
  -F health=10.0/10.0
# → raw 24 kHz, 16-bit mono PCM; metadata in X-MineYapping-* headers

To confirm the wiring without spending tokens, run with a bogus OPENAI_API_KEY: a well-formed request comes back 502 carrying the upstream invalid_api_key message, which proves everything up to the provider call works.

Docker

docker compose up -d --build   # API :31415, web :4001 on loopback
docker compose logs -f server

DEPLOY.md in the repository covers a full VPS deployment behind Caddy with managed PostgreSQL.

Building the mod from source

No JDK install needed — the Gradle wrapper downloads JDK 21 (Adoptium) on first run, on both macOS and Windows.

cd minecraft-mod
./gradlew build        # → build/libs/mineyapping-<version>.jar
./gradlew runClient    # launches Minecraft with the mod loaded

In IntelliJ, Open the minecraft-mod folder as its own Gradle project and use the generated Minecraft Client run configuration.

Pointing the mod at your server

Edit config/mine-yapping.json with Minecraft closed:

{
  "apiKey": "",
  "serverUrl": "http://localhost:31415/api/converse",
  "speechChance": 0.5
}

Then create a key on your own dashboard at http://localhost:4001 and run /login <token> in game.

Repo scripts

ScriptDescription
bun run devStart all apps in development
bun run dev:serverServer only
bun run dev:adminWeb app only
bun run buildBuild all applications
bun run check-typestsc across all workspaces
bun run checkBiome format + lint, writes fixes
bun testUnit tests (scope with bun test apps/server/src to skip stale dist/ copies)
bun run db:pushPush schema changes
bun run db:generateGenerate migrations
bun run db:migrateRun migrations
bun run db:studioOpen Drizzle Studio

Known limitations

  • Android is unsupported. Capture uses javax.sound.sampled, which has no working mixer on the Android OpenJDK builds PojavLauncher-family launchers ship. Moving capture to LWJGL's OpenAL (ALC11.alcCaptureOpenDevice) is the intended fix — Minecraft already bundles it everywhere, and it would enable spatial playback too.
  • Conversation history is in-process — lost on restart, not shared across instances.
  • The server port is fixed at 31415.

On this page