Operational guide for AI agents working in this repo. Long-form human docs:
CONTRIBUTING.md (setup, testing philosophy) and frontend/README.md.
Read those before large changes. This file is the short, must-follow version.
module github.com/mhsanaei/3x-ui/v3), Gin, GORM.
Runs Xray-core as a managed child process (internal/xray/process.go) and
imports github.com/xtls/xray-core for config types + gRPC stats/handler/router
API. MTProto inbounds run a second managed child — the mtg binary
(internal/mtproto/) — outside Xray./etc/x-ui/x-ui.db on Linux; the executable dir on
Windows), PostgreSQL optional (XUI_DB_TYPE / XUI_DB_DSN). The CGo SQLite
driver (mattn/go-sqlite3) needs a C compiler — CGO_ENABLED=0 builds fail.frontend/,
built into internal/web/dist/ (gitignored) and embedded via embed.FS.main.go — entry point + x-ui CLI (run, migrate, migrate-db, setting, cert).internal/config/ — env parsing (XUI_DEBUG, XUI_LOG_LEVEL, XUI_LOG_FOLDER,
XUI_BIN_FOLDER, XUI_SKIP_HSTS, XUI_PORT, XUIDB*).internal/database/ + internal/database/model/ — GORM schema (Inbound,
Client, Setting, User), inbound Protocol enum, AutoMigrate + hand-written
migrations in db.go.internal/xray/ — Xray child-process lifecycle, config generation, gRPC API.internal/mtproto/ — MTProto inbounds via the bundled mtg binary.internal/sub/ — subscription server (raw / JSON / Clash).internal/eventbus/ — in-process pub/sub (outbound/node health, xray.crash,
cpu.high, memory.high, login.attempt).internal/logger/, internal/util/ (link, crypto, sys, ldap, …),
internal/tunnelmonitor/ — shared infrastructure.internal/web/ — Gin server (embeds dist/ + translation/).
controller/ — panel + REST API handlers; OpenAPI at /panel/api/openapi.json.service/ — business logic (InboundService, SettingService, XrayService,
node sync); subpackages tgbot/, email/, outbound/, panel/, integration/.job/ — cron jobs (traffic, fail2ban IP-limit, node heartbeat/sync, LDAP).middleware/, entity/, global/, session/ (CSRF), network/,
runtime/ (master/sub-node over mTLS), websocket/.locale/ + translation/ — i18n, 13 embedded locale JSON files.frontend/ — React + TS source (see frontend/CLAUDE.md).tools/openapigen/ — Go generator that emits frontend types + Zod/JSON schemas
into frontend/src/generated/ from Go structs. The OpenAPI doc itself
(frontend/public/openapi.json) is assembled from those + endpoints.ts by
frontend/scripts/build-openapi.mjs.// line comments in committed Go/TS. Names carry meaning; rename instead
of annotating. Exempt: //go:build, //go:generate, and other directives.
HTML <!-- --> is fine. (A linter cannot enforce this — you must.)g.POST/g.GET in internal/web/controller/ REQUIRES a matching entry
in frontend/src/pages/api-docs/endpoints.ts, then make gen (or
cd frontend && npm run gen). It is a hand-maintained registry — nothing checks
it against the Go routes, so an omitted route silently vanishes from the docs.example: tags via tools/openapigen —
never hand-write them. A new struct must be added to openapigen's StructAllow
allowlist (tools/openapigen/main.go) or it is silently omitted from
schemas/examples (and build-openapi.mjs then fails on the missing schema).internal/web/translation/ (13 files). Missing keys fall back to en-US (or
render the raw key if absent there too); nothing fails the build, so they are
easy to miss.internal/database/db.go.feat, fix, refactor, chore, docs,
style): <area>: short imperative summary, then a body explaining the why.testing only (no testify). Table-driven, t.Run subtests,
t.Helper() on helpers. Assert the exact value / typed error / emitted
string, never just err != nil. Prefer real deps over mocks: throwaway DB via
database.InitDB(filepath.Join(t.TempDir(), "x-ui.db")) +
t.Cleanup(func() { _ = database.CloseDB() }); httptest for HTTP.
internal/sub's initSubDB(t) is the template.golangci-lint run (gofumpt + goimports formatting): make lint.@typescript-eslint/no-explicit-any is an error. Zod schemas in
src/schemas/ are the source of truth; infer types with z.infer, never
hand-write. Do not edit src/generated/.frontend/src does NOT change what users see until the Vite build is
regenerated into internal/web/dist/. In XUI_DEBUG=true, HTML is served from
the frozen embedded FS but JS/CSS off disk — after npm run build you MUST
restart go run . or you get a blank page with 404s.src/lib/xray/), run npm run test (golden
fixtures); regenerate snapshots (npx vitest run -u) only for intentional
output changes, never to make a red test green.Run make help for all targets. The full local gate that mirrors CI:
make verify
Common targets: make gen (regenerate Zod/OpenAPI), make lint (Go + frontend),
make test (Go -shuffle=on + frontend), make race, make build. See Makefile.
make gen and confirm git diff on frontend/src/generated +
frontend/public/openapi.json is clean.make verify passes.