Database Schema for Inbounds and Outbounds in S-UI: Complete Technical Reference
S-UI stores routing configurations in a SQLite database using GORM models, with inbounds and outbounds tables that combine fixed relational columns and JSON blobs for protocol-specific settings.
S-UI is a web-based management interface for V2Ray/Xray proxies built by Alireza. Understanding the database schema for inbounds and outbounds in S-UI is essential for developers extending the platform, migrating configurations, or troubleshooting routing logic. The schema follows a hybrid relational/JSON design that balances query performance with the flexibility needed to support diverse proxy protocols.
Inbounds Table Structure
The inbounds table, defined in database/model/inbounds.go, stores incoming connection configurations:
| Column | Go Type | SQLite Type | Description |
|---|---|---|---|
id |
uint |
INTEGER PRIMARY KEY AUTOINCREMENT |
Primary key generated by GORM |
type |
string |
TEXT |
Protocol identifier (e.g., vmess, trojan, vless) |
tag |
string |
TEXT UNIQUE |
Human-readable unique identifier |
tls_id |
uint |
INTEGER |
Foreign key referencing tls.id |
addrs |
json.RawMessage |
BLOB |
JSON-encoded listening addresses |
out_json |
json.RawMessage |
BLOB |
Embedded outbound reference as JSON |
options |
json.RawMessage |
BLOB |
Protocol-specific configuration blob |
TLS Relationship
The tls_id column establishes a one-to-one relationship with the tls table. This allows each inbound to reference dedicated TLS settings stored separately in the database. When tls_id equals 0, the inbound operates without TLS encryption.
Outbounds Table Structure
Located in database/model/outbounds.go, the outbounds table uses a simplified schema optimized for egress routing:
| Column | Go Type | SQLite Type | Description |
|---|---|---|---|
id |
uint |
INTEGER PRIMARY KEY AUTOINCREMENT |
Primary key |
type |
string |
TEXT |
Outbound protocol (e.g., direct, freedom, socks) |
tag |
string |
TEXT UNIQUE |
Unique identifier for routing rules |
options |
json.RawMessage |
BLOB |
JSON-encoded server configuration |
Unlike inbounds, outbounds do not require foreign key relationships. All protocol-specific data—including server addresses, authentication credentials, and transport settings—resides in the options JSON column.
GORM Model Definitions
Both tables are implemented as Go structs with GORM annotations. In database/model/inbounds.go, the Inbound struct implements custom marshaling:
type Inbound struct {
ID uint `gorm:"primaryKey"`
Type string `gorm:"type:text"`
Tag string `gorm:"type:text;uniqueIndex"`
TlsId uint `gorm:"column:tls_id"`
Addrs json.RawMessage `gorm:"type:blob"`
OutJson json.RawMessage `gorm:"type:blob"`
Options json.RawMessage `gorm:"type:blob"`
}
The Outbound struct in database/model/outbounds.go follows a similar pattern but omits the TLS and address fields, reflecting the asymmetric configuration requirements between inbound listeners and outbound proxies.
Database Initialization and Migration
S-UI automatically creates these tables during startup. In database/db.go, the AutoMigrate function ensures schema consistency:
db.AutoMigrate(&model.Inbound{}, &model.Outbound{}, …)
This migration also seeds a default entry. If the outbounds table is empty, S-UI inserts a direct outbound automatically, ensuring basic connectivity exists immediately after installation.
JSON Serialization Strategy
The schema relies heavily on custom MarshalJSON and UnmarshalJSON methods. These methods separate fixed relational fields from variable protocol configurations:
- Fixed fields (
id,type,tag,tls_id) map to database columns for querying and indexing - Dynamic fields pack into
json.RawMessageblobs, allowing S-UI to support new V2Ray-compatible protocols without schema migrations
When the API serves configuration data, the MarshalFull() method (implemented on the Inbound struct) merges the fixed fields with the JSON blobs to produce complete configuration objects.
Practical Query Examples
Creating an Inbound
To insert a new VMess inbound with specific options:
in := model.Inbound{
Type: "vmess",
Tag: "vmess-inbound",
TlsId: 0,
Addrs: json.RawMessage(`["0.0.0.0:1080"]`),
OutJson: json.RawMessage(`{"tag":"direct"}`),
Options: json.RawMessage(`{
"clients": [{"id":"uuid-here","alterId":0}]
}`),
}
if err := database.GetDB().Create(&in).Error; err != nil {
// handle error
}
Reading Outbound Configuration
To retrieve and decode an outbound's settings:
var out model.Outbound
if err := database.GetDB().Where("tag = ?", "direct").First(&out).Error; err != nil {
// handle error
}
var opts map[string]interface{}
json.Unmarshal(out.Options, &opts)
// opts now contains the full protocol configuration
Full JSON Marshaling
To generate complete configuration output including all fields:
// Standard marshaling (fixed fields only)
b, _ := json.Marshal(in)
// Full marshaling (includes id, tls_id, addrs, out_json, and options)
full, _ := in.MarshalFull()
Summary
- S-UI uses SQLite with GORM models for configuration persistence
- The
inboundstable includes seven columns: metadata fields plusaddrs,out_json, andoptionsJSON blobs - The
outboundstable uses four columns:id,type,tag, and anoptionsblob - Foreign key:
inbounds.tls_idreferencestls.idfor encryption settings - Auto-migration occurs in
database/db.gowith defaultdirectoutbound seeding - Custom JSON marshaling preserves fixed columns while supporting flexible protocol extensions
Frequently Asked Questions
How does S-UI handle protocol-specific settings without database schema changes?
S-UI stores variable configuration data in json.RawMessage blobs within the options column (and addrs/out_json for inbounds). Custom MarshalJSON methods pack protocol-specific fields into these BLOB columns, while fixed fields remain as queryable database columns. This hybrid approach accommodates new V2Ray protocols without requiring ALTER TABLE operations.
What is the relationship between inbounds and TLS configurations?
The inbounds table includes a tls_id column that functions as a foreign key to the tls table. This creates a one-to-one relationship where each inbound optionally references TLS settings (certificates, encryption modes) stored in a separate table. A tls_id value of 0 indicates the inbound uses no TLS encryption.
Where does S-UI initialize the database schema?
Database initialization occurs in database/db.go through GORM's AutoMigrate function, which creates the inbounds and outbounds tables if they don't exist. The same file handles default data seeding, inserting a direct outbound entry automatically when the database is first created.
Can I query inbound listening addresses using SQL?
While the addrs column stores data as a BLOB containing JSON (e.g., ["0.0.0.0:1080"]), SQLite can query JSON content using the json_extract function. However, S-UI typically reads these values into Go structs and unmarshals them via json.RawMessage rather than performing complex SQL queries on the JSON blobs.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →