Get started

Installation

Tiyi is distributed as a signed Linux binary. Install it from the public release channel, write a small YAML config, and start the complete Controller runtime or enroll a remote Agent.

Replacing v3.4.0 or earlier with v3.5.3 requires a destructive full uninstall. Stop the old service, make and verify a complete backup, run sudo tiyi uninstall --purge, then install a fresh v3.5.3 service and re-enroll every Agent. Do not restore the old database into v3.5. Follow the dedicated upgrade guide before installing.

Install the signed binary

Tiyi is one signed binary — free and full-featured on a single node. The canonical install script is published at https://www.tiyisec.com/install.sh:

$ curl -fsSL https://www.tiyisec.com/install.sh | bash
$ tiyi --version
tiyi v3.5.3 linux/amd64

The script downloads the platform-matched binary, verifies its SHA-256 against the manifest, and drops it into /usr/local/bin/tiyi. It tries GitHub first and falls back to the Gitee release mirror; force Gitee with TIYI_MIRROR=gitee when needed:

$ curl -fsSL https://gitee.com/tiyisec/tiyi/raw/main/install.sh | TIYI_MIRROR=gitee bash

Installer environment overrides: TIYI_MIRROR=auto|github|gitee, TIYI_REPO=zzmzm/tiyi, TIYI_GITEE_REPO=tiyisec/tiyi, TIYI_VERSION=v3.5.3, and TIYI_PREFIX=/usr/local/bin.

Public distribution repos: GitHub zzmzm/tiyi and Gitee tiyisec/tiyi.

linux/amd64 and linux/arm64 are first-class targets. Install the signed release from the public channel independently on every Controller, Agent, and dashboard host before configuring its role.

Config file

Tiyi reads a Koanf-based YAML config. Start from this minimal config and let the defaults handle the rest:

# /etc/tiyi/tiyi.yaml
server:
  addr: "0.0.0.0:8080"

store:
  state_db: "/var/lib/tiyi/state.db"

crypto:
  kek_file: "/etc/tiyi/kek.bin"      # 32-byte at-rest encryption key

auth:
  jwt_secret: "<32+ random bytes>"      # HS256 signing secret

proxy:
  http_addr:  ":80"
  https_addr: ":443"
  caddy_admin_socket: "/var/lib/tiyi/caddy-admin.sock"

Pass it with --config. This config uses /var/lib/tiyi and ports 80/443, so run it as root (or under the systemd unit below):

$ sudo tiyi run --config /etc/tiyi/tiyi.yaml || sudo /usr/local/bin/tiyi run --config /etc/tiyi/tiyi.yaml

To try Tiyi as a normal user without sudo, skip the config file and pass writable paths and high ports on the command line instead — see the Quickstart.

Two fields are mandatory before going live. crypto.kek_file and auth.jwt_secret have dev-friendly fallbacks that explicitly do not survive a restart. See Deployment → Production hardening for the contract.

Config environment variables

Prefer /etc/tiyi/tiyi.yaml for persistent service configuration. Use environment variables only when your service manager, container runtime, or secret manager injects config at runtime. Precedence is built-in defaults, then tiyi.yaml, then config environment variables, then explicit CLI flags.

Environment names mirror config keys: prefix TIYI_, uppercase the key, and replace dots with underscores. For example, auth.jwt_secret becomes TIYI_AUTH_JWT_SECRET.

Common config overrides

VariableConfig keyWhen to use
TIYI_SERVER_ADDRserver.addrBind the API/dashboard to a different address.
TIYI_STORE_STATE_DBstore.state_dbMove the SQLite state database.
TIYI_LOG_LEVELlog.levelTemporarily raise or lower process logging.
TIYI_PROXY_HTTP_ADDRproxy.http_addrChange the HTTP data-plane listen address.
TIYI_PROXY_HTTPS_ADDRproxy.https_addrChange the HTTPS data-plane listen address.
TIYI_PROXY_CADDY_ADMIN_SOCKETproxy.caddy_admin_socketMove the embedded Caddy admin socket.
TIYI_CRYPTO_KEK_FILEcrypto.kek_filePin the at-rest encryption KEK path for production.
TIYI_AUTH_JWT_SECRETauth.jwt_secretSet a stable JWT signing secret for production.
TIYI_AUTH_BOOTSTRAP_ADMIN_USERNAMEauth.bootstrap_admin_usernameChoose the first admin username.
TIYI_AUTH_BOOTSTRAP_ADMIN_PASSWORDauth.bootstrap_admin_passwordChoose the first admin password for automation.
TIYI_LICENSE_KEY_PATHlicense.key_pathLoad a signed license file on boot.
TIYI_UPDATE_REPOupdate.repoOverride the GitHub release repo used by update checks.
TIYI_UPDATE_CHANNELupdate.channelUse stable or prerelease for tiyi update.
TIYI_UPDATE_MIRRORupdate.mirrorUse auto, github, or gitee for update checks/downloads.

Less common config keys follow the same rule, including auth.access_token_ttl, auth.refresh_token_ttl, auth.refresh_cookie_secure, auth.bootstrap_admin_email, auth.bootstrap_admin_name, auth.provider, auth.ldap.*, auth.radius.*, and update.api_base_url. Prefer YAML for these unless your deployment platform requires env injection.

Runtime commands

The same binary has one complete runtime and two focused processes:

run
The Controller, built-in local data plane, API, and admin UI in one writable process. This is the normal install and five-minute path.
agent
A remote data plane: Caddy + Coraza receiving signed config bundles from one Controller URL over a long-lived ConnectRPC stream.
dashboard
A UI-only process for a host that does not own state or proxy traffic.

Enrolling an agent

Open Nodes → Install, issue a token, then follow the separate binary-download and systemd instructions. The same page also shows the raw token, a foreground command, and a complete download-and-start script:

$ sudo curl -fsSL -o /usr/local/bin/tiyi 'https://tiyi.example.com/download/tiyi'
$ sudo chmod 0755 /usr/local/bin/tiyi
$ sudo mkdir -p /etc/tiyi
$ printf 'TIYI_CONTROLLER_URL=https://tiyi.example.com\nTIYI_AGENT_ENROLLMENT_TOKEN=<one-use-token>\n' | sudo tee /etc/tiyi/tiyi-agent.env >/dev/null
$ sudo chmod 0600 /etc/tiyi/tiyi-agent.env
$ sudo tiyi install --mode agent --unit-name tiyi-agent --now

Lose the state directory and you'll need a fresh token to re-enroll.

Run as a systemd service

For a long-lived deployment, let Tiyi install itself as a hardened systemd service. tiyi install creates the tiyi service user and the tiyi-admin socket group, writes the unit, and with --now enables and starts it on boot:

$ sudo tiyi install --now

Running sudo tiyi install without --now intentionally leaves the unit disabled and stopped so you can finish mode-specific configuration. For the first Controller start, re-run the same install command with --now added; do not start the unit directly with systemctl. The installer can then create the administrator first and print the one-time login here instead of sending the password to the system journal.

The service runs unprivileged and binds ports 80/443 via CAP_NET_BIND_SERVICE. Before each start, a root-only pre-start hook re-owns the configured state directory and KEK file for the service user, so CRS caches, uploads, exclusion packages, release staging, logs, trust snapshots, and embedded-agent state left by a prior sudo foreground run cannot block runtime writes. On a first install, --now pre-creates the administrator and prints the one-time login itself — no journal scraping:

tiyi is enabled and running as tiyi.service.

  Tiyi administrator created
    URL:       http://127.0.0.1:8080
    username:  admin
    password:  <generated-password>

  This password is shown once; change it after signing in.

Preview the generated unit without touching the system using tiyi install --print. Its key directives:

# /etc/systemd/system/tiyi.service
[Service]
Type=simple
User=tiyi
Group=tiyi
PermissionsStartOnly=true
ExecStartPre=/usr/local/bin/tiyi systemd-prestart --config /etc/tiyi/tiyi.yaml --user tiyi --group tiyi
ExecStart=/usr/local/bin/tiyi run --config /etc/tiyi/tiyi.yaml --admin-socket /run/tiyi/admin.sock --admin-socket-group tiyi-admin --admin-socket-mode 0660 --caddy-admin-socket /run/tiyi/caddy.sock
Restart=on-failure
AmbientCapabilities=CAP_NET_BIND_SERVICE
CapabilityBoundingSet=CAP_NET_BIND_SERVICE
StateDirectory=tiyi
RuntimeDirectory=tiyi
ConfigurationDirectory=tiyi
NoNewPrivileges=yes
ProtectSystem=strict
ProtectHome=yes
PrivateTmp=yes

[Install]
WantedBy=multi-user.target

Run tiyi doctor to check sudo PATH, systemd, config loading, installed-service state ownership, and listeners on ports 80/443/8080. If no systemd unit is installed, state ownership is skipped unless you pass --service-user/--service-group or request repair. If it warns that sudo cannot find tiyi, use the printed full-path command or add /usr/local/bin to sudo secure_path. If it reports root-owned state, repair it explicitly with sudo tiyi doctor --fix-state-ownership. If it reports a port conflict, either stop the owning service or move Tiyi with proxy.http_addr, proxy.https_addr, or server.addr. Select --mode run for the Controller or --mode agent for a remote data plane. Remove the service with sudo tiyi uninstall: state under /var/lib/tiyi, config under /etc/tiyi, and the service user are preserved unless you add --purge.

What to read next