Skip to content

Bill a REALITY client to a SOCKS user, so its tier applies - #18

Merged
GlobalTechInfo merged 1 commit into
mainfrom
feat/reality-per-user
Oct 18, 2026
Merged

GlobalTechInfo merged 1 commit into
mainfrom
feat/reality-per-user

Conversation

@GlobalTechInfo

Copy link
Copy Markdown
Owner

The gap

Every REALITY session was handed to the SOCKS5 port on the service's shared credentials. No tier, limit, counter or byte ever applied to REALITY traffic: a paying customer's front door and a free one's looked identical from here, and neither was metered.

What it does

xray routes by the email on the VLESS client, so each linked client gets an email, a socks outbound carrying its own credentials, and a rule joining the two. Its sessions then arrive as an ordinary user and inherit the whole pipeline — tier, limits, accounting, the user list. Clients that are not linked fall through to the shared outbound, so nothing changes for anyone who hasn't asked for it.

The part that made this safe

xray needs a readable password in its config; the users table must not hold one, since PR #11 started hashing them. A per-install secret satisfies both: generated once, the username and password are recomputed on every render, and only the hash is ever written. No readable password is kept anywhere — including in git, which is where a hardcoded one would otherwise end up.

Verified end to end, against real xray

connecting to target client=127.0.0.1 user=xr1 tier=free target=api.ipify.org

A session through a linked client arrives authenticated as its own user on its own tier, where before it arrived as the shared credentials.

The test asserts the same chain without needing a network: the link is recorded, the user exists at the tier asked for, the derived password verifies against the stored hash, the rendered config carries the email, the per-user outbound and the rule, and xray itself accepts that config (xray run --test). Unlinking puts the client back on the shared credentials.

Design notes

  • Unlinked clients keep working rather than being refused — the shared outbound stays as the fallback rule.
  • Relinking is idempotent: an existing linked user keeps its tier rather than being recreated, so changing the tier on the user row is not undone by re-linking.
  • The linked user appears in the ordinary user list, where its tier is changed like anyone else's.

Cost, stated rather than discovered later

Each linked client adds one outbound and one rule to the xray config. A few hundred is fine; beyond that this wants rethinking rather than growing. It's in the README.

gofmt/go vet clean; full suite green with and without xray; -race clean.

Every REALITY session was handed to the SOCKS5 port on the service's shared
credentials, so no tier, limit, counter or byte ever applied to REALITY
traffic. A paying customer's front door and a free one's looked identical
from here, and neither was metered.

xray routes by the email on the VLESS client, so each linked client gets an
email, a socks outbound carrying its own credentials, and a rule joining the
two. Its sessions then arrive as an ordinary user and inherit the whole
pipeline. Clients that are not linked fall through to the shared outbound,
so nothing changes for anyone who has not asked for it.

The credentials are derived rather than stored, which is the part that made
this safe to do alongside password hashing. xray needs a readable password,
the users table must not hold one, and a per-install secret satisfies both:
it is generated once, the username and password are recomputed on every
render, and only the hash is ever written. Nothing readable is kept.

Verified end to end against real xray: a session through a linked client
arrives authenticated as its own user, on its own tier --

    connecting to target client=127.0.0.1 user=xr1 tier=free target=api.ipify.org

The test asserts the same chain without a network: the link is recorded, the
user exists at the tier asked for, the derived password verifies against the
stored hash, the rendered config carries the email, the per-user outbound
and the rule, and xray itself accepts that config. Unlinking puts the
client back on the shared credentials.

The cost is stated in the README rather than left to be discovered: each
linked client adds an outbound and a rule, so a very large number of clients
makes the config big.
@coderabbitai

coderabbitai Bot commented Oct 18, 2026

Copy link
Copy Markdown

Warning

Review limit reached

You've used all free OSS reviews for now. Wait for the free limit to reset to keep reviewing this public repository.

Next included review available in 47 minutes.

Check out review usage here.

View limit details

Limit details: You’ve used the included review currently available.

Learn how review limits work.

Review configuration:

⚙️ Run configuration
  • Configuration used: defaults
  • Review profile: CHILL
  • Plan: Advanced
  • Run ID: 2494ecf6-ae3b-46a4-9346-9a42b4db27bc
📥 Commits

Reviewing files that changed from the base of the PR and between 62f9f33 and 1b46fbe.

📒 Files selected for processing: 6
  • README.md
  • database.go
  • main.go
  • reality.go
  • reality_test.go
  • web/index.html
  • Autofix · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@GlobalTechInfo
GlobalTechInfo merged commit 963319f into main Oct 18, 2026
3 checks passed
@GlobalTechInfo
GlobalTechInfo deleted the feat/reality-per-user branch October 18, 2026 09:59
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant