Socks-family inbounds authenticate against settings.accounts, so the
clients attached to them never reached the core: the generated config
carried the settings' "clients" mirror (ignored by xray for these
protocols, passwords and all) and only the inbound's own fixed
account. Emit one account per enabled attached client instead —
user = client email, pass = client password — appended after the
inbound's stored accounts, which keep their order and win name
collisions (managed-forwarding resolution reads the fixed account
from the stored settings, untouched). Drop the clients mirror from
the generated settings, and force auth=password when clients exist,
since accounts are inert under noauth.
Accounting needs no new plumbing: xray-core 26.6.27 reports these
accounts under user>>><account user> counters (probe-verified with
two accounts on both mixed and socks), and the agent snapshot ->
report -> AddTraffic chain already keys client traffic by that exact
string, so making the account name the client email aligns booking
end to end. Clientless socks-family inbounds pass through verbatim.
Design as decided 2026-10-21 (replaces the raw-JSON egress editor idea):
the forwarding form lives on the inbound, and the entry agent's egress
entries are compiled from it on every config pull — never snapshotted.
- model: Inbound.Forwarding (JSON text) via AutoMigrate; panel-side
metadata only, never reaches xray itself
- service/forwarding.go: ForwardingSpec (mode direct/forward; landing
managed|manual), save-time validation (managed target must exist and
be a SOCKS-family landing — the fork's SK5 inbound is 'mixed'; no
self-forward; manual endpoint/protocol sanity), live resolution
(managed: concrete listen IP wins, else hosting node's address;
credentials from the target's first account), and the compiler that
appends one socks/http outbound (tag fwd-<inboundId>) plus a prepended
inboundTag rule per forwarding inbound
- buildXrayConfig agent path: compile runs after applyAgentEgress, so
entries ride on the live template (or stored egress override) and
template edits keep flowing; unresolvable entries skip with a warning
instead of failing the whole config
- UpdateInbound's field-merge now carries Forwarding (its absence was
caught by the smoke: adds worked, updates silently kept the old spec)
- GET /panel/api/inbounds/forwardTargets: managed-landing candidates
(id/remark/tag/listen/port/node name) — credentials never exposed
- frontend: new Forwarding tab in the inbound form (mode, landing type,
managed picker, manual endpoint with link import incl. base64 form),
adapter round-trips the spec as a JSON string like settings; en/zh
strings; generated API types refreshed
Acceptance: unit tests for parse/validate/resolve/compile incl. the
dynamic-recompile proof (template edit + spec edit both reflected), and
an end-to-end smoke (real binary, two agent nodes, all inbound writes
in browser form-urlencoded format): 22/22 PASS — see P9-REPORT.md.